Commits:
-
d339153b
by Pier Angelo Vendrame at 2023-08-07T18:36:05+02:00
fixup! Bug 40933: Add tor-launcher functionality
Use the new functions whenever possible, adjust some property names and
other minor fixes.
-
e8d3c0b8
by Pier Angelo Vendrame at 2023-08-07T18:36:14+02:00
fixup! Bug 40597: Implement TorSettings module
Changes needed for the new control port implementation.
Also, moved to ES modules and done some refactors on Moat.
-
c1b05a65
by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
Changes for the new control port implementation and following
ESMification.
-
d23cb300
by Pier Angelo Vendrame at 2023-08-07T18:36:15+02:00
fixup! Bug 30237: Add v3 onion services client authentication prompt
Changes for the new control port implementation.
-
c801d620
by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 7494: Create local home page for TBB.
Use the TorProvider in TorCheckService
-
fa105e4f
by Pier Angelo Vendrame at 2023-08-07T18:36:16+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.
Remove TorMonitorService and TorProtocolService references.
-
cd92f30e
by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 41668: Tweaks to the Base Browser updater for Tor Browser
Removed TorMonitorService reference
-
384dff97
by Pier Angelo Vendrame at 2023-08-07T18:36:17+02:00
fixup! Bug 40933: Add tor-launcher functionality
Remove the final references to TorMonitorService and TorProtocolService.
24 changed files:
Changes:
browser/components/abouttor/TorCheckService.sys.mjs
... |
... |
@@ -11,14 +11,9 @@ const lazy = {}; |
11
|
11
|
|
12
|
12
|
ChromeUtils.defineESModuleGetters(lazy, {
|
13
|
13
|
ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
|
|
14
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
14
|
15
|
});
|
15
|
16
|
|
16
|
|
-ChromeUtils.defineModuleGetter(
|
17
|
|
- lazy,
|
18
|
|
- "TorProtocolService",
|
19
|
|
- "resource://gre/modules/TorProtocolService.jsm"
|
20
|
|
-);
|
21
|
|
-
|
22
|
17
|
export const TorCheckService = {
|
23
|
18
|
kCheckNotInitiated: 0, // Possible values for status.
|
24
|
19
|
kCheckSuccessful: 1,
|
... |
... |
@@ -109,7 +104,7 @@ export const TorCheckService = { |
109
|
104
|
|
110
|
105
|
let listeners;
|
111
|
106
|
try {
|
112
|
|
- listeners = await lazy.TorProtocolService.getSocksListeners();
|
|
107
|
+ listeners = await lazy.TorProviderBuilder.build().getSocksListeners();
|
113
|
108
|
} catch (e) {
|
114
|
109
|
this._logger.error("Failed to get the SOCKS listerner addresses.", e);
|
115
|
110
|
return false;
|
browser/components/onionservices/content/authPrompt.js
... |
... |
@@ -4,10 +4,13 @@ |
4
|
4
|
|
5
|
5
|
/* globals gBrowser, PopupNotifications, Services, XPCOMUtils */
|
6
|
6
|
|
|
7
|
+ChromeUtils.defineESModuleGetters(this, {
|
|
8
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
9
|
+});
|
|
10
|
+
|
7
|
11
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
8
|
12
|
OnionAuthUtil: "chrome://browser/content/onionservices/authUtil.jsm",
|
9
|
13
|
CommonUtils: "resource://services-common/utils.js",
|
10
|
|
- TorProtocolService: "resource://gre/modules/TorProtocolService.jsm",
|
11
|
14
|
TorStrings: "resource:///modules/TorStrings.jsm",
|
12
|
15
|
});
|
13
|
16
|
|
... |
... |
@@ -203,7 +206,8 @@ const _OnionAuthPrompt_ = (function () { |
203
|
206
|
|
204
|
207
|
let checkboxElem = this._getCheckboxElement();
|
205
|
208
|
let isPermanent = checkboxElem && checkboxElem.checked;
|
206
|
|
- TorProtocolService.onionAuthAdd(onionServiceId, base64key, isPermanent)
|
|
209
|
+ TorProviderBuilder.build()
|
|
210
|
+ .onionAuthAdd(onionServiceId, base64key, isPermanent)
|
207
|
211
|
.then(aResponse => {
|
208
|
212
|
// Success! Reload the page.
|
209
|
213
|
this._browser.sendMessageToActor(
|
browser/components/onionservices/content/savedKeysDialog.js
... |
... |
@@ -8,11 +8,9 @@ ChromeUtils.defineModuleGetter( |
8
|
8
|
"resource:///modules/TorStrings.jsm"
|
9
|
9
|
);
|
10
|
10
|
|
11
|
|
-ChromeUtils.defineModuleGetter(
|
12
|
|
- this,
|
13
|
|
- "TorProtocolService",
|
14
|
|
- "resource://gre/modules/TorProtocolService.jsm"
|
15
|
|
-);
|
|
11
|
+ChromeUtils.defineESModuleGetters(this, {
|
|
12
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
13
|
+});
|
16
|
14
|
|
17
|
15
|
var gOnionServicesSavedKeysDialog = {
|
18
|
16
|
selector: {
|
... |
... |
@@ -54,6 +52,7 @@ var gOnionServicesSavedKeysDialog = { |
54
|
52
|
await this._deleteOneKey(indexesToDelete[i]);
|
55
|
53
|
}
|
56
|
54
|
} catch (e) {
|
|
55
|
+ console.error("Removing a saved key failed", e);
|
57
|
56
|
if (e.torMessage) {
|
58
|
57
|
this._showError(e.torMessage);
|
59
|
58
|
} else {
|
... |
... |
@@ -125,22 +124,16 @@ var gOnionServicesSavedKeysDialog = { |
125
|
124
|
try {
|
126
|
125
|
this._tree.view = this;
|
127
|
126
|
|
128
|
|
- const keyInfoList = await TorProtocolService.onionAuthViewKeys();
|
|
127
|
+ const keyInfoList = await TorProviderBuilder.build().onionAuthViewKeys();
|
129
|
128
|
if (keyInfoList) {
|
130
|
129
|
// Filter out temporary keys.
|
131
|
|
- this._keyInfoList = keyInfoList.filter(aKeyInfo => {
|
132
|
|
- if (!aKeyInfo.Flags) {
|
133
|
|
- return false;
|
134
|
|
- }
|
135
|
|
-
|
136
|
|
- const flags = aKeyInfo.Flags.split(",");
|
137
|
|
- return flags.includes("Permanent");
|
138
|
|
- });
|
139
|
|
-
|
|
130
|
+ this._keyInfoList = keyInfoList.filter(aKeyInfo =>
|
|
131
|
+ aKeyInfo.flags?.includes("Permanent")
|
|
132
|
+ );
|
140
|
133
|
// Sort by the .onion address.
|
141
|
134
|
this._keyInfoList.sort((aObj1, aObj2) => {
|
142
|
|
- const hsAddr1 = aObj1.hsAddress.toLowerCase();
|
143
|
|
- const hsAddr2 = aObj2.hsAddress.toLowerCase();
|
|
135
|
+ const hsAddr1 = aObj1.address.toLowerCase();
|
|
136
|
+ const hsAddr2 = aObj2.address.toLowerCase();
|
144
|
137
|
if (hsAddr1 < hsAddr2) {
|
145
|
138
|
return -1;
|
146
|
139
|
}
|
... |
... |
@@ -164,7 +157,7 @@ var gOnionServicesSavedKeysDialog = { |
164
|
157
|
// This method may throw; callers should catch errors.
|
165
|
158
|
async _deleteOneKey(aIndex) {
|
166
|
159
|
const keyInfoObj = this._keyInfoList[aIndex];
|
167
|
|
- await TorProtocolService.onionAuthRemove(keyInfoObj.hsAddress);
|
|
160
|
+ await TorProviderBuilder.build().onionAuthRemove(keyInfoObj.address);
|
168
|
161
|
this._tree.view.selection.clearRange(aIndex, aIndex);
|
169
|
162
|
this._keyInfoList.splice(aIndex, 1);
|
170
|
163
|
this._tree.rowCountChanged(aIndex + 1, -1);
|
... |
... |
@@ -193,26 +186,20 @@ var gOnionServicesSavedKeysDialog = { |
193
|
186
|
|
194
|
187
|
// XUL tree widget view implementation.
|
195
|
188
|
get rowCount() {
|
196
|
|
- return this._keyInfoList ? this._keyInfoList.length : 0;
|
|
189
|
+ return this._keyInfoList?.length ?? 0;
|
197
|
190
|
},
|
198
|
191
|
|
199
|
192
|
getCellText(aRow, aCol) {
|
200
|
|
- let val = "";
|
201
|
193
|
if (this._keyInfoList && aRow < this._keyInfoList.length) {
|
202
|
194
|
const keyInfo = this._keyInfoList[aRow];
|
203
|
195
|
if (aCol.id.endsWith("-siteCol")) {
|
204
|
|
- val = keyInfo.hsAddress;
|
|
196
|
+ return keyInfo.address;
|
205
|
197
|
} else if (aCol.id.endsWith("-keyCol")) {
|
206
|
|
- val = keyInfo.typeAndKey;
|
207
|
|
- // Omit keyType because it is always "x25519".
|
208
|
|
- const idx = val.indexOf(":");
|
209
|
|
- if (idx > 0) {
|
210
|
|
- val = val.substring(idx + 1);
|
211
|
|
- }
|
|
198
|
+ // keyType is always "x25519", so do not show it.
|
|
199
|
+ return keyInfo.keyBlob;
|
212
|
200
|
}
|
213
|
201
|
}
|
214
|
|
-
|
215
|
|
- return val;
|
|
202
|
+ return "";
|
216
|
203
|
},
|
217
|
204
|
|
218
|
205
|
isSeparator(index) {
|
browser/components/torpreferences/content/builtinBridgeDialog.jsm
... |
... |
@@ -7,10 +7,10 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
7
|
7
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
8
|
8
|
|
9
|
9
|
const { TorSettings, TorBridgeSource, TorBuiltinBridgeTypes } =
|
10
|
|
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
|
|
10
|
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
|
11
|
11
|
|
12
|
|
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
|
13
|
|
- "resource:///modules/TorConnect.jsm"
|
|
12
|
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
|
|
13
|
+ "resource:///modules/TorConnect.sys.mjs"
|
14
|
14
|
);
|
15
|
15
|
|
16
|
16
|
class BuiltinBridgeDialog {
|
browser/components/torpreferences/content/connectionPane.js
... |
... |
@@ -12,20 +12,17 @@ const { setTimeout, clearTimeout } = ChromeUtils.import( |
12
|
12
|
);
|
13
|
13
|
|
14
|
14
|
const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
|
15
|
|
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
|
|
15
|
+ ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
|
16
|
16
|
|
17
|
17
|
const { TorParsers } = ChromeUtils.importESModule(
|
18
|
18
|
"resource://gre/modules/TorParsers.sys.mjs"
|
19
|
19
|
);
|
20
|
|
-const { TorProtocolService } = ChromeUtils.importESModule(
|
21
|
|
- "resource://gre/modules/TorProtocolService.sys.mjs"
|
22
|
|
-);
|
23
|
|
-const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
|
24
|
|
- "resource://gre/modules/TorMonitorService.jsm"
|
|
20
|
+const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
|
|
21
|
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
|
25
|
22
|
);
|
26
|
23
|
|
27
|
24
|
const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } =
|
28
|
|
- ChromeUtils.import("resource:///modules/TorConnect.jsm");
|
|
25
|
+ ChromeUtils.importESModule("resource:///modules/TorConnect.sys.mjs");
|
29
|
26
|
|
30
|
27
|
const { TorLogDialog } = ChromeUtils.import(
|
31
|
28
|
"chrome://browser/content/torpreferences/torLogDialog.jsm"
|
... |
... |
@@ -51,7 +48,9 @@ const { ProvideBridgeDialog } = ChromeUtils.import( |
51
|
48
|
"chrome://browser/content/torpreferences/provideBridgeDialog.jsm"
|
52
|
49
|
);
|
53
|
50
|
|
54
|
|
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
|
|
51
|
+const { MoatRPC } = ChromeUtils.importESModule(
|
|
52
|
+ "resource:///modules/Moat.sys.mjs"
|
|
53
|
+);
|
55
|
54
|
|
56
|
55
|
const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm");
|
57
|
56
|
|
... |
... |
@@ -156,7 +155,7 @@ const gConnectionPane = (function () { |
156
|
155
|
_populateXUL() {
|
157
|
156
|
// saves tor settings to disk when navigate away from about:preferences
|
158
|
157
|
window.addEventListener("blur", val => {
|
159
|
|
- TorProtocolService.flushSettings();
|
|
158
|
+ TorProviderBuilder.build().flushSettings();
|
160
|
159
|
});
|
161
|
160
|
|
162
|
161
|
document
|
... |
... |
@@ -751,7 +750,7 @@ const gConnectionPane = (function () { |
751
|
750
|
// TODO: We could make sure TorSettings is in sync by monitoring also
|
752
|
751
|
// changes of settings. At that point, we could query it, instead of
|
753
|
752
|
// doing a query over the control port.
|
754
|
|
- const bridge = TorMonitorService.currentBridge;
|
|
753
|
+ const bridge = TorProviderBuilder.build().currentBridge;
|
755
|
754
|
if (bridge?.fingerprint !== this._currentBridgeId) {
|
756
|
755
|
this._currentBridgeId = bridge?.fingerprint ?? null;
|
757
|
756
|
this._updateConnectedBridges();
|
... |
... |
@@ -850,7 +849,7 @@ const gConnectionPane = (function () { |
850
|
849
|
});
|
851
|
850
|
|
852
|
851
|
Services.obs.addObserver(this, TorConnectTopics.StateChange);
|
853
|
|
- Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
|
|
852
|
+ Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
|
854
|
853
|
Services.obs.addObserver(this, "intl:app-locales-changed");
|
855
|
854
|
},
|
856
|
855
|
|
... |
... |
@@ -875,7 +874,7 @@ const gConnectionPane = (function () { |
875
|
874
|
// unregister our observer topics
|
876
|
875
|
Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
|
877
|
876
|
Services.obs.removeObserver(this, TorConnectTopics.StateChange);
|
878
|
|
- Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
|
|
877
|
+ Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
|
879
|
878
|
Services.obs.removeObserver(this, "intl:app-locales-changed");
|
880
|
879
|
},
|
881
|
880
|
|
... |
... |
@@ -907,7 +906,7 @@ const gConnectionPane = (function () { |
907
|
906
|
this.onStateChange();
|
908
|
907
|
break;
|
909
|
908
|
}
|
910
|
|
- case TorMonitorTopics.BridgeChanged: {
|
|
909
|
+ case TorProviderTopics.BridgeChanged: {
|
911
|
910
|
if (data?.fingerprint !== this._currentBridgeId) {
|
912
|
911
|
this._checkConnectedBridge();
|
913
|
912
|
}
|
browser/components/torpreferences/content/connectionSettingsDialog.jsm
... |
... |
@@ -2,8 +2,8 @@ |
2
|
2
|
|
3
|
3
|
var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"];
|
4
|
4
|
|
5
|
|
-const { TorSettings, TorProxyType } = ChromeUtils.import(
|
6
|
|
- "resource:///modules/TorSettings.jsm"
|
|
5
|
+const { TorSettings, TorProxyType } = ChromeUtils.importESModule(
|
|
6
|
+ "resource:///modules/TorSettings.sys.mjs"
|
7
|
7
|
);
|
8
|
8
|
|
9
|
9
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
browser/components/torpreferences/content/provideBridgeDialog.jsm
... |
... |
@@ -6,12 +6,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
6
|
6
|
|
7
|
7
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
8
|
8
|
|
9
|
|
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
|
10
|
|
- "resource:///modules/TorSettings.jsm"
|
|
9
|
+const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule(
|
|
10
|
+ "resource:///modules/TorSettings.sys.mjs"
|
11
|
11
|
);
|
12
|
12
|
|
13
|
|
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
|
14
|
|
- "resource:///modules/TorConnect.jsm"
|
|
13
|
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
|
|
14
|
+ "resource:///modules/TorConnect.sys.mjs"
|
15
|
15
|
);
|
16
|
16
|
|
17
|
17
|
class ProvideBridgeDialog {
|
browser/components/torpreferences/content/requestBridgeDialog.jsm
... |
... |
@@ -4,11 +4,13 @@ var EXPORTED_SYMBOLS = ["RequestBridgeDialog"]; |
4
|
4
|
|
5
|
5
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
6
|
6
|
|
7
|
|
-const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
|
|
7
|
+const { BridgeDB } = ChromeUtils.importESModule(
|
|
8
|
+ "resource:///modules/BridgeDB.sys.mjs"
|
|
9
|
+);
|
8
|
10
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
9
|
11
|
|
10
|
|
-const { TorConnect, TorConnectTopics } = ChromeUtils.import(
|
11
|
|
- "resource:///modules/TorConnect.jsm"
|
|
12
|
+const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
|
|
13
|
+ "resource:///modules/TorConnect.sys.mjs"
|
12
|
14
|
);
|
13
|
15
|
|
14
|
16
|
class RequestBridgeDialog {
|
browser/components/torpreferences/content/torLogDialog.jsm
... |
... |
@@ -2,12 +2,12 @@ |
2
|
2
|
|
3
|
3
|
var EXPORTED_SYMBOLS = ["TorLogDialog"];
|
4
|
4
|
|
5
|
|
-const { setTimeout, clearTimeout } = ChromeUtils.import(
|
6
|
|
- "resource://gre/modules/Timer.jsm"
|
|
5
|
+const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
|
|
6
|
+ "resource://gre/modules/Timer.sys.mjs"
|
7
|
7
|
);
|
8
|
8
|
|
9
|
|
-const { TorMonitorService } = ChromeUtils.import(
|
10
|
|
- "resource://gre/modules/TorMonitorService.jsm"
|
|
9
|
+const { TorProviderBuilder } = ChromeUtils.importESModule(
|
|
10
|
+ "resource://gre/modules/TorProviderBuilder.sys.mjs"
|
11
|
11
|
);
|
12
|
12
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
13
|
13
|
|
... |
... |
@@ -56,7 +56,7 @@ class TorLogDialog { |
56
|
56
|
}, RESTORE_TIME);
|
57
|
57
|
});
|
58
|
58
|
|
59
|
|
- this._logTextarea.value = TorMonitorService.getLog();
|
|
59
|
+ this._logTextarea.value = TorProviderBuilder.build().getLog();
|
60
|
60
|
}
|
61
|
61
|
|
62
|
62
|
init(window, aDialog) {
|
browser/modules/BridgeDB.jsm
→
browser/modules/BridgeDB.sys.mjs
1
|
|
-"use strict";
|
|
1
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2
|
4
|
|
3
|
|
-var EXPORTED_SYMBOLS = ["BridgeDB"];
|
|
5
|
+const lazy = {};
|
4
|
6
|
|
5
|
|
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
|
|
7
|
+ChromeUtils.defineESModuleGetters(lazy, {
|
|
8
|
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
|
|
9
|
+});
|
6
|
10
|
|
7
|
|
-var BridgeDB = {
|
|
11
|
+export var BridgeDB = {
|
8
|
12
|
_moatRPC: null,
|
9
|
13
|
_challenge: null,
|
10
|
14
|
_image: null,
|
... |
... |
@@ -20,7 +24,7 @@ var BridgeDB = { |
20
|
24
|
|
21
|
25
|
async submitCaptchaGuess(solution) {
|
22
|
26
|
if (!this._moatRPC) {
|
23
|
|
- this._moatRPC = new MoatRPC();
|
|
27
|
+ this._moatRPC = new lazy.MoatRPC();
|
24
|
28
|
await this._moatRPC.init();
|
25
|
29
|
}
|
26
|
30
|
|
... |
... |
@@ -37,7 +41,7 @@ var BridgeDB = { |
37
|
41
|
async requestNewCaptchaImage() {
|
38
|
42
|
try {
|
39
|
43
|
if (!this._moatRPC) {
|
40
|
|
- this._moatRPC = new MoatRPC();
|
|
44
|
+ this._moatRPC = new lazy.MoatRPC();
|
41
|
45
|
await this._moatRPC.init();
|
42
|
46
|
}
|
43
|
47
|
|
browser/modules/Moat.jsm
→
browser/modules/Moat.sys.mjs
1
|
|
-"use strict";
|
|
1
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2
|
4
|
|
3
|
|
-var EXPORTED_SYMBOLS = ["MoatRPC"];
|
|
5
|
+import {
|
|
6
|
+ TorSettings,
|
|
7
|
+ TorBridgeSource,
|
|
8
|
+} from "resource:///modules/TorSettings.sys.mjs";
|
4
|
9
|
|
5
|
|
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
10
|
+const lazy = {};
|
6
|
11
|
|
7
|
|
-const { Subprocess } = ChromeUtils.import(
|
8
|
|
- "resource://gre/modules/Subprocess.jsm"
|
9
|
|
-);
|
10
|
|
-
|
11
|
|
-const { TorLauncherUtil } = ChromeUtils.import(
|
12
|
|
- "resource://gre/modules/TorLauncherUtil.jsm"
|
13
|
|
-);
|
14
|
|
-
|
15
|
|
-const { TorProtocolService } = ChromeUtils.import(
|
16
|
|
- "resource://gre/modules/TorProtocolService.jsm"
|
17
|
|
-);
|
18
|
|
-
|
19
|
|
-const { TorSettings, TorBridgeSource } = ChromeUtils.import(
|
20
|
|
- "resource:///modules/TorSettings.jsm"
|
21
|
|
-);
|
|
12
|
+ChromeUtils.defineESModuleGetters(lazy, {
|
|
13
|
+ Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
|
|
14
|
+ TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
|
|
15
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
16
|
+});
|
22
|
17
|
|
23
|
18
|
const TorLauncherPrefs = Object.freeze({
|
24
|
19
|
bridgedb_front: "extensions.torlauncher.bridgedb_front",
|
... |
... |
@@ -26,73 +21,54 @@ const TorLauncherPrefs = Object.freeze({ |
26
|
21
|
moat_service: "extensions.torlauncher.moat_service",
|
27
|
22
|
});
|
28
|
23
|
|
29
|
|
-// Config keys used to query tor daemon properties
|
30
|
|
-const TorConfigKeys = Object.freeze({
|
31
|
|
- clientTransportPlugin: "ClientTransportPlugin",
|
32
|
|
-});
|
33
|
|
-
|
34
|
24
|
//
|
35
|
25
|
// Launches and controls the PT process lifetime
|
36
|
26
|
//
|
37
|
27
|
class MeekTransport {
|
38
|
|
- constructor() {
|
39
|
|
- this._inited = false;
|
40
|
|
- this._meekClientProcess = null;
|
41
|
|
- this._meekProxyType = null;
|
42
|
|
- this._meekProxyAddress = null;
|
43
|
|
- this._meekProxyPort = 0;
|
44
|
|
- this._meekProxyUsername = null;
|
45
|
|
- this._meekProxyPassword = null;
|
46
|
|
- }
|
|
28
|
+ // These members are used by consumers to setup the proxy to do requests over
|
|
29
|
+ // meek. They are passed to newProxyInfoWithAuth.
|
|
30
|
+ proxyType = null;
|
|
31
|
+ proxyAddress = null;
|
|
32
|
+ proxyPort = 0;
|
|
33
|
+ proxyUsername = null;
|
|
34
|
+ proxyPassword = null;
|
|
35
|
+
|
|
36
|
+ #inited = false;
|
|
37
|
+ #meekClientProcess = null;
|
47
|
38
|
|
48
|
39
|
// launches the meekprocess
|
49
|
40
|
async init() {
|
50
|
41
|
// ensure we haven't already init'd
|
51
|
|
- if (this._inited) {
|
|
42
|
+ if (this.#inited) {
|
52
|
43
|
throw new Error("MeekTransport: Already initialized");
|
53
|
44
|
}
|
54
|
45
|
|
55
|
|
- // cleanup function for killing orphaned pt process
|
56
|
|
- let _onException_ = () => {};
|
57
|
46
|
try {
|
58
|
47
|
// figure out which pluggable transport to use
|
59
|
48
|
const supportedTransports = ["meek", "meek_lite"];
|
60
|
|
- let transportPlugins = await TorProtocolService.readStringArraySetting(
|
61
|
|
- TorConfigKeys.clientTransportPlugin
|
|
49
|
+ const proxy = (
|
|
50
|
+ await lazy.TorProviderBuilder.build().getPluggableTransports()
|
|
51
|
+ ).find(
|
|
52
|
+ pt =>
|
|
53
|
+ pt.type === "exec" &&
|
|
54
|
+ supportedTransports.some(t => pt.transports.includes(t))
|
62
|
55
|
);
|
|
56
|
+ if (!proxy) {
|
|
57
|
+ throw new Error("No supported transport found.");
|
|
58
|
+ }
|
63
|
59
|
|
64
|
|
- let { meekTransport, meekClientPath, meekClientArgs } = (() => {
|
65
|
|
- for (const line of transportPlugins) {
|
66
|
|
- let tokens = line.split(" ");
|
67
|
|
- if (tokens.length > 2 && tokens[1] == "exec") {
|
68
|
|
- let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
|
69
|
|
- let transport = transportArray.find(aTransport =>
|
70
|
|
- supportedTransports.includes(aTransport)
|
71
|
|
- );
|
72
|
|
-
|
73
|
|
- if (transport != undefined) {
|
74
|
|
- return {
|
75
|
|
- meekTransport: transport,
|
76
|
|
- meekClientPath: tokens[2],
|
77
|
|
- meekClientArgs: tokens.slice(3),
|
78
|
|
- };
|
79
|
|
- }
|
80
|
|
- }
|
81
|
|
- }
|
82
|
|
-
|
83
|
|
- return {
|
84
|
|
- meekTransport: null,
|
85
|
|
- meekClientPath: null,
|
86
|
|
- meekClientArgs: null,
|
87
|
|
- };
|
88
|
|
- })();
|
89
|
|
-
|
|
60
|
+ const meekTransport = proxy.transports.find(t =>
|
|
61
|
+ supportedTransports.includes(t)
|
|
62
|
+ );
|
90
|
63
|
// Convert meek client path to absolute path if necessary
|
91
|
|
- let meekWorkDir = TorLauncherUtil.getTorFile("pt-startup-dir", false);
|
92
|
|
- if (TorLauncherUtil.isPathRelative(meekClientPath)) {
|
93
|
|
- let meekPath = meekWorkDir.clone();
|
94
|
|
- meekPath.appendRelativePath(meekClientPath);
|
95
|
|
- meekClientPath = meekPath.path;
|
|
64
|
+ const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
|
|
65
|
+ "pt-startup-dir",
|
|
66
|
+ false
|
|
67
|
+ );
|
|
68
|
+ if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
|
|
69
|
+ const meekPath = meekWorkDir.clone();
|
|
70
|
+ meekPath.appendRelativePath(proxy.pathToBinary);
|
|
71
|
+ proxy.pathToBinary = meekPath.path;
|
96
|
72
|
}
|
97
|
73
|
|
98
|
74
|
// Construct the per-connection arguments.
|
... |
... |
@@ -105,16 +81,13 @@ class MeekTransport { |
105
|
81
|
// First the "<Key>=<Value>" formatted arguments MUST be escaped,
|
106
|
82
|
// such that all backslash, equal sign, and semicolon characters
|
107
|
83
|
// are escaped with a backslash.
|
108
|
|
- let escapeArgValue = aValue => {
|
109
|
|
- if (!aValue) {
|
110
|
|
- return "";
|
111
|
|
- }
|
112
|
|
-
|
113
|
|
- let rv = aValue.replace(/\\/g, "\\\\");
|
114
|
|
- rv = rv.replace(/=/g, "\\=");
|
115
|
|
- rv = rv.replace(/;/g, "\\;");
|
116
|
|
- return rv;
|
117
|
|
- };
|
|
84
|
+ const escapeArgValue = aValue =>
|
|
85
|
+ aValue
|
|
86
|
+ ? aValue
|
|
87
|
+ .replaceAll("\\", "\\\\")
|
|
88
|
+ .replaceAll("=", "\\=")
|
|
89
|
+ .replaceAll(";", "\\;")
|
|
90
|
+ : "";
|
118
|
91
|
|
119
|
92
|
if (meekReflector) {
|
120
|
93
|
meekClientEscapedArgs += "url="">";
|
... |
... |
@@ -132,10 +105,10 @@ class MeekTransport { |
132
|
105
|
}
|
133
|
106
|
|
134
|
107
|
// Setup env and start meek process
|
135
|
|
- let ptStateDir = TorLauncherUtil.getTorFile("tordatadir", false);
|
|
108
|
+ const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
|
136
|
109
|
ptStateDir.append("pt_state"); // Match what tor uses.
|
137
|
110
|
|
138
|
|
- let envAdditions = {
|
|
111
|
+ const envAdditions = {
|
139
|
112
|
TOR_PT_MANAGED_TRANSPORT_VER: "1",
|
140
|
113
|
TOR_PT_STATE_LOCATION: ptStateDir.path,
|
141
|
114
|
TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
|
... |
... |
@@ -145,9 +118,9 @@ class MeekTransport { |
145
|
118
|
envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
|
146
|
119
|
}
|
147
|
120
|
|
148
|
|
- let opts = {
|
149
|
|
- command: meekClientPath,
|
150
|
|
- arguments: meekClientArgs,
|
|
121
|
+ const opts = {
|
|
122
|
+ command: proxy.pathToBinary,
|
|
123
|
+ arguments: proxy.options.split(/s+/),
|
151
|
124
|
workdir: meekWorkDir.path,
|
152
|
125
|
environmentAppend: true,
|
153
|
126
|
environment: envAdditions,
|
... |
... |
@@ -155,27 +128,23 @@ class MeekTransport { |
155
|
128
|
};
|
156
|
129
|
|
157
|
130
|
// Launch meek client
|
158
|
|
- let meekClientProcess = await Subprocess.call(opts);
|
159
|
|
- // kill our process if exception is thrown
|
160
|
|
- _onException_ = () => {
|
161
|
|
- meekClientProcess.kill();
|
162
|
|
- };
|
|
131
|
+ this.#meekClientProcess = await lazy.Subprocess.call(opts);
|
163
|
132
|
|
164
|
133
|
// Callback chain for reading stderr
|
165
|
|
- let stderrLogger = async () => {
|
166
|
|
- if (this._meekClientProcess) {
|
167
|
|
- let errString = await this._meekClientProcess.stderr.readString();
|
168
|
|
- console.log(`MeekTransport: stderr => ${errString}`);
|
169
|
|
- await stderrLogger();
|
|
134
|
+ const stderrLogger = async () => {
|
|
135
|
+ while (this.#meekClientProcess) {
|
|
136
|
+ const errString = await this.#meekClientProcess.stderr.readString();
|
|
137
|
+ if (errString) {
|
|
138
|
+ console.log(`MeekTransport: stderr => ${errString}`);
|
|
139
|
+ }
|
170
|
140
|
}
|
171
|
141
|
};
|
172
|
142
|
stderrLogger();
|
173
|
143
|
|
174
|
144
|
// Read pt's stdout until terminal (CMETHODS DONE) is reached
|
175
|
145
|
// returns array of lines for parsing
|
176
|
|
- let getInitLines = async (stdout = "") => {
|
177
|
|
- let string = await meekClientProcess.stdout.readString();
|
178
|
|
- stdout += string;
|
|
146
|
+ const getInitLines = async (stdout = "") => {
|
|
147
|
+ stdout += await this.#meekClientProcess.stdout.readString();
|
179
|
148
|
|
180
|
149
|
// look for the final message
|
181
|
150
|
const CMETHODS_DONE = "CMETHODS DONE";
|
... |
... |
@@ -188,20 +157,16 @@ class MeekTransport { |
188
|
157
|
};
|
189
|
158
|
|
190
|
159
|
// read our lines from pt's stdout
|
191
|
|
- let meekInitLines = await getInitLines();
|
|
160
|
+ const meekInitLines = await getInitLines();
|
192
|
161
|
// tokenize our pt lines
|
193
|
|
- let meekInitTokens = meekInitLines.map(line => {
|
194
|
|
- let tokens = line.split(" ");
|
|
162
|
+ const meekInitTokens = meekInitLines.map(line => {
|
|
163
|
+ const tokens = line.split(" ");
|
195
|
164
|
return {
|
196
|
165
|
keyword: tokens[0],
|
197
|
166
|
args: tokens.slice(1),
|
198
|
167
|
};
|
199
|
168
|
});
|
200
|
169
|
|
201
|
|
- let meekProxyType = null;
|
202
|
|
- let meekProxyAddr = null;
|
203
|
|
- let meekProxyPort = 0;
|
204
|
|
-
|
205
|
170
|
// parse our pt tokens
|
206
|
171
|
for (const { keyword, args } of meekInitTokens) {
|
207
|
172
|
const argsJoined = args.join(" ");
|
... |
... |
@@ -251,9 +216,9 @@ class MeekTransport { |
251
|
216
|
}
|
252
|
217
|
|
253
|
218
|
// convert proxy type to strings used by protocol-proxy-servce
|
254
|
|
- meekProxyType = proxyType === "socks5" ? "socks" : "socks4";
|
255
|
|
- meekProxyAddr = addr;
|
256
|
|
- meekProxyPort = port;
|
|
219
|
+ this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
|
|
220
|
+ this.proxyAddress = addr;
|
|
221
|
+ this.proxyPort = port;
|
257
|
222
|
|
258
|
223
|
break;
|
259
|
224
|
}
|
... |
... |
@@ -278,49 +243,47 @@ class MeekTransport { |
278
|
243
|
}
|
279
|
244
|
}
|
280
|
245
|
|
281
|
|
- this._meekClientProcess = meekClientProcess;
|
282
|
246
|
// register callback to cleanup on process exit
|
283
|
|
- this._meekClientProcess.wait().then(exitObj => {
|
284
|
|
- this._meekClientProcess = null;
|
|
247
|
+ this.#meekClientProcess.wait().then(exitObj => {
|
|
248
|
+ this.#meekClientProcess = null;
|
285
|
249
|
this.uninit();
|
286
|
250
|
});
|
287
|
251
|
|
288
|
|
- this._meekProxyType = meekProxyType;
|
289
|
|
- this._meekProxyAddress = meekProxyAddr;
|
290
|
|
- this._meekProxyPort = meekProxyPort;
|
291
|
|
-
|
292
|
252
|
// socks5
|
293
|
|
- if (meekProxyType === "socks") {
|
|
253
|
+ if (this.proxyType === "socks") {
|
294
|
254
|
if (meekClientEscapedArgs.length <= 255) {
|
295
|
|
- this._meekProxyUsername = meekClientEscapedArgs;
|
296
|
|
- this._meekProxyPassword = "\x00";
|
|
255
|
+ this.proxyUsername = meekClientEscapedArgs;
|
|
256
|
+ this.proxyPassword = "\x00";
|
297
|
257
|
} else {
|
298
|
|
- this._meekProxyUsername = meekClientEscapedArgs.substring(0, 255);
|
299
|
|
- this._meekProxyPassword = meekClientEscapedArgs.substring(255);
|
|
258
|
+ this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
|
|
259
|
+ this.proxyPassword = meekClientEscapedArgs.substring(255);
|
300
|
260
|
}
|
301
|
261
|
// socks4
|
302
|
262
|
} else {
|
303
|
|
- this._meekProxyUsername = meekClientEscapedArgs;
|
304
|
|
- this._meekProxyPassword = undefined;
|
|
263
|
+ this.proxyUsername = meekClientEscapedArgs;
|
|
264
|
+ this.proxyPassword = undefined;
|
305
|
265
|
}
|
306
|
266
|
|
307
|
|
- this._inited = true;
|
|
267
|
+ this.#inited = true;
|
308
|
268
|
} catch (ex) {
|
309
|
|
- onException();
|
|
269
|
+ if (this.#meekClientProcess) {
|
|
270
|
+ this.#meekClientProcess.kill();
|
|
271
|
+ this.#meekClientProcess = null;
|
|
272
|
+ }
|
310
|
273
|
throw ex;
|
311
|
274
|
}
|
312
|
275
|
}
|
313
|
276
|
|
314
|
277
|
async uninit() {
|
315
|
|
- this._inited = false;
|
316
|
|
-
|
317
|
|
- await this._meekClientProcess?.kill();
|
318
|
|
- this._meekClientProcess = null;
|
319
|
|
- this._meekProxyType = null;
|
320
|
|
- this._meekProxyAddress = null;
|
321
|
|
- this._meekProxyPort = 0;
|
322
|
|
- this._meekProxyUsername = null;
|
323
|
|
- this._meekProxyPassword = null;
|
|
278
|
+ this.#inited = false;
|
|
279
|
+
|
|
280
|
+ await this.#meekClientProcess?.kill();
|
|
281
|
+ this.#meekClientProcess = null;
|
|
282
|
+ this.proxyType = null;
|
|
283
|
+ this.proxyAddress = null;
|
|
284
|
+ this.proxyPort = 0;
|
|
285
|
+ this.proxyUsername = null;
|
|
286
|
+ this.proxyPassword = null;
|
324
|
287
|
}
|
325
|
288
|
}
|
326
|
289
|
|
... |
... |
@@ -328,21 +291,25 @@ class MeekTransport { |
328
|
291
|
// Callback object with a cached promise for the returned Moat data
|
329
|
292
|
//
|
330
|
293
|
class MoatResponseListener {
|
|
294
|
+ #response = "";
|
|
295
|
+ #responsePromise;
|
|
296
|
+ #resolve;
|
|
297
|
+ #reject;
|
331
|
298
|
constructor() {
|
332
|
|
- this._response = "";
|
|
299
|
+ this.#response = "";
|
333
|
300
|
// we need this promise here because await nsIHttpChannel::asyncOpen does
|
334
|
301
|
// not return only once the request is complete, it seems to return
|
335
|
302
|
// after it begins, so we have to get the result from this listener object.
|
336
|
303
|
// This promise is only resolved once onStopRequest is called
|
337
|
|
- this._responsePromise = new Promise((resolve, reject) => {
|
338
|
|
- this._resolve = resolve;
|
339
|
|
- this._reject = reject;
|
|
304
|
+ this.#responsePromise = new Promise((resolve, reject) => {
|
|
305
|
+ this.#resolve = resolve;
|
|
306
|
+ this.#reject = reject;
|
340
|
307
|
});
|
341
|
308
|
}
|
342
|
309
|
|
343
|
310
|
// callers wait on this for final response
|
344
|
311
|
response() {
|
345
|
|
- return this._responsePromise;
|
|
312
|
+ return this.#responsePromise;
|
346
|
313
|
}
|
347
|
314
|
|
348
|
315
|
// noop
|
... |
... |
@@ -352,16 +319,17 @@ class MoatResponseListener { |
352
|
319
|
onStopRequest(request, status) {
|
353
|
320
|
try {
|
354
|
321
|
if (!Components.isSuccessCode(status)) {
|
355
|
|
- const errorMessage = TorLauncherUtil.getLocalizedStringForError(status);
|
356
|
|
- this._reject(new Error(errorMessage));
|
|
322
|
+ const errorMessage =
|
|
323
|
+ lazy.TorLauncherUtil.getLocalizedStringForError(status);
|
|
324
|
+ this.#reject(new Error(errorMessage));
|
357
|
325
|
}
|
358
|
326
|
if (request.responseStatus != 200) {
|
359
|
|
- this._reject(new Error(request.responseStatusText));
|
|
327
|
+ this.#reject(new Error(request.responseStatusText));
|
360
|
328
|
}
|
361
|
329
|
} catch (err) {
|
362
|
|
- this._reject(err);
|
|
330
|
+ this.#reject(err);
|
363
|
331
|
}
|
364
|
|
- this._resolve(this._response);
|
|
332
|
+ this.#resolve(this.#response);
|
365
|
333
|
}
|
366
|
334
|
|
367
|
335
|
// read response data
|
... |
... |
@@ -370,30 +338,32 @@ class MoatResponseListener { |
370
|
338
|
"@mozilla.org/scriptableinputstream;1"
|
371
|
339
|
].createInstance(Ci.nsIScriptableInputStream);
|
372
|
340
|
scriptableStream.init(stream);
|
373
|
|
- this._response += scriptableStream.read(length);
|
|
341
|
+ this.#response += scriptableStream.read(length);
|
374
|
342
|
}
|
375
|
343
|
}
|
376
|
344
|
|
377
|
345
|
class InternetTestResponseListener {
|
|
346
|
+ #promise;
|
|
347
|
+ #resolve;
|
|
348
|
+ #reject;
|
378
|
349
|
constructor() {
|
379
|
|
- this._promise = new Promise((resolve, reject) => {
|
380
|
|
- this._resolve = resolve;
|
381
|
|
- this._reject = reject;
|
|
350
|
+ this.#promise = new Promise((resolve, reject) => {
|
|
351
|
+ this.#resolve = resolve;
|
|
352
|
+ this.#reject = reject;
|
382
|
353
|
});
|
383
|
354
|
}
|
384
|
355
|
|
385
|
356
|
// callers wait on this for final response
|
386
|
357
|
get status() {
|
387
|
|
- return this._promise;
|
|
358
|
+ return this.#promise;
|
388
|
359
|
}
|
389
|
360
|
|
390
|
361
|
onStartRequest(request) {}
|
391
|
362
|
|
392
|
363
|
// resolve or reject our Promise
|
393
|
364
|
onStopRequest(request, status) {
|
394
|
|
- let statuses = {};
|
395
|
365
|
try {
|
396
|
|
- statuses = {
|
|
366
|
+ const statuses = {
|
397
|
367
|
components: status,
|
398
|
368
|
successful: Components.isSuccessCode(status),
|
399
|
369
|
};
|
... |
... |
@@ -408,56 +378,51 @@ class InternetTestResponseListener { |
408
|
378
|
err
|
409
|
379
|
);
|
410
|
380
|
}
|
|
381
|
+ this.#resolve(statuses);
|
411
|
382
|
} catch (err) {
|
412
|
|
- this._reject(err);
|
|
383
|
+ this.#reject(err);
|
413
|
384
|
}
|
414
|
|
- this._resolve(statuses);
|
415
|
385
|
}
|
416
|
386
|
|
417
|
387
|
onDataAvailable(request, stream, offset, length) {
|
418
|
|
- // We do not care of the actual data, as long as we have a successful
|
|
388
|
+ // We do not care of the actual data, as long as we have a successful
|
419
|
389
|
// connection
|
420
|
390
|
}
|
421
|
391
|
}
|
422
|
392
|
|
423
|
393
|
// constructs the json objects and sends the request over moat
|
424
|
|
-class MoatRPC {
|
425
|
|
- constructor() {
|
426
|
|
- this._meekTransport = null;
|
427
|
|
- this._inited = false;
|
428
|
|
- }
|
|
394
|
+export class MoatRPC {
|
|
395
|
+ #inited = false;
|
|
396
|
+ #meekTransport = null;
|
429
|
397
|
|
430
|
398
|
get inited() {
|
431
|
|
- return this._inited;
|
|
399
|
+ return this.#inited;
|
432
|
400
|
}
|
433
|
401
|
|
434
|
402
|
async init() {
|
435
|
|
- if (this._inited) {
|
|
403
|
+ if (this.#inited) {
|
436
|
404
|
throw new Error("MoatRPC: Already initialized");
|
437
|
405
|
}
|
438
|
406
|
|
439
|
407
|
let meekTransport = new MeekTransport();
|
440
|
408
|
await meekTransport.init();
|
441
|
|
- this._meekTransport = meekTransport;
|
442
|
|
- this._inited = true;
|
|
409
|
+ this.#meekTransport = meekTransport;
|
|
410
|
+ this.#inited = true;
|
443
|
411
|
}
|
444
|
412
|
|
445
|
413
|
async uninit() {
|
446
|
|
- await this._meekTransport?.uninit();
|
447
|
|
- this._meekTransport = null;
|
448
|
|
- this._inited = false;
|
|
414
|
+ await this.#meekTransport?.uninit();
|
|
415
|
+ this.#meekTransport = null;
|
|
416
|
+ this.#inited = false;
|
449
|
417
|
}
|
450
|
418
|
|
451
|
|
- _makeHttpHandler(uriString) {
|
452
|
|
- if (!this._inited) {
|
|
419
|
+ #makeHttpHandler(uriString) {
|
|
420
|
+ if (!this.#inited) {
|
453
|
421
|
throw new Error("MoatRPC: Not initialized");
|
454
|
422
|
}
|
455
|
423
|
|
456
|
|
- const proxyType = this._meekTransport._meekProxyType;
|
457
|
|
- const proxyAddress = this._meekTransport._meekProxyAddress;
|
458
|
|
- const proxyPort = this._meekTransport._meekProxyPort;
|
459
|
|
- const proxyUsername = this._meekTransport._meekProxyUsername;
|
460
|
|
- const proxyPassword = this._meekTransport._meekProxyPassword;
|
|
424
|
+ const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
|
|
425
|
+ this.#meekTransport;
|
461
|
426
|
|
462
|
427
|
const proxyPS = Cc[
|
463
|
428
|
"@mozilla.org/network/protocol-proxy-service;1"
|
... |
... |
@@ -511,11 +476,11 @@ class MoatRPC { |
511
|
476
|
return ch;
|
512
|
477
|
}
|
513
|
478
|
|
514
|
|
- async _makeRequest(procedure, args) {
|
|
479
|
+ async #makeRequest(procedure, args) {
|
515
|
480
|
const procedureURIString = `${Services.prefs.getStringPref(
|
516
|
481
|
TorLauncherPrefs.moat_service
|
517
|
482
|
)}/${procedure}`;
|
518
|
|
- const ch = this._makeHttpHandler(procedureURIString);
|
|
483
|
+ const ch = this.#makeHttpHandler(procedureURIString);
|
519
|
484
|
|
520
|
485
|
// Arrange for the POST data to be sent.
|
521
|
486
|
const argsJson = JSON.stringify(args);
|
... |
... |
@@ -544,7 +509,7 @@ class MoatRPC { |
544
|
509
|
const uri = `${Services.prefs.getStringPref(
|
545
|
510
|
TorLauncherPrefs.moat_service
|
546
|
511
|
)}/circumvention/countries`;
|
547
|
|
- const ch = this._makeHttpHandler(uri);
|
|
512
|
+ const ch = this.#makeHttpHandler(uri);
|
548
|
513
|
ch.requestMethod = "HEAD";
|
549
|
514
|
|
550
|
515
|
const listener = new InternetTestResponseListener();
|
... |
... |
@@ -582,7 +547,7 @@ class MoatRPC { |
582
|
547
|
},
|
583
|
548
|
],
|
584
|
549
|
};
|
585
|
|
- const response = await this._makeRequest("fetch", args);
|
|
550
|
+ const response = await this.#makeRequest("fetch", args);
|
586
|
551
|
if ("errors" in response) {
|
587
|
552
|
const code = response.errors[0].code;
|
588
|
553
|
const detail = response.errors[0].detail;
|
... |
... |
@@ -623,7 +588,7 @@ class MoatRPC { |
623
|
588
|
},
|
624
|
589
|
],
|
625
|
590
|
};
|
626
|
|
- const response = await this._makeRequest("check", args);
|
|
591
|
+ const response = await this.#makeRequest("check", args);
|
627
|
592
|
if ("errors" in response) {
|
628
|
593
|
const code = response.errors[0].code;
|
629
|
594
|
const detail = response.errors[0].detail;
|
... |
... |
@@ -642,7 +607,7 @@ class MoatRPC { |
642
|
607
|
|
643
|
608
|
// Convert received settings object to format used by TorSettings module
|
644
|
609
|
// In the event of error, just return null
|
645
|
|
- _fixupSettings(settings) {
|
|
610
|
+ #fixupSettings(settings) {
|
646
|
611
|
try {
|
647
|
612
|
let retval = TorSettings.defaultSettings();
|
648
|
613
|
if ("bridges" in settings) {
|
... |
... |
@@ -691,11 +656,11 @@ class MoatRPC { |
691
|
656
|
// Converts a list of settings objects received from BridgeDB to a list of settings objects
|
692
|
657
|
// understood by the TorSettings module
|
693
|
658
|
// In the event of error, returns and empty list
|
694
|
|
- _fixupSettingsList(settingsList) {
|
|
659
|
+ #fixupSettingsList(settingsList) {
|
695
|
660
|
try {
|
696
|
661
|
let retval = [];
|
697
|
662
|
for (let settings of settingsList) {
|
698
|
|
- settings = this._fixupSettings(settings);
|
|
663
|
+ settings = this.#fixupSettings(settings);
|
699
|
664
|
if (settings != null) {
|
700
|
665
|
retval.push(settings);
|
701
|
666
|
}
|
... |
... |
@@ -724,7 +689,7 @@ class MoatRPC { |
724
|
689
|
transports: transports ? transports : [],
|
725
|
690
|
country,
|
726
|
691
|
};
|
727
|
|
- const response = await this._makeRequest("circumvention/settings", args);
|
|
692
|
+ const response = await this.#makeRequest("circumvention/settings", args);
|
728
|
693
|
let settings = {};
|
729
|
694
|
if ("errors" in response) {
|
730
|
695
|
const code = response.errors[0].code;
|
... |
... |
@@ -739,7 +704,7 @@ class MoatRPC { |
739
|
704
|
|
740
|
705
|
throw new Error(`MoatRPC: ${detail} (${code})`);
|
741
|
706
|
} else if ("settings" in response) {
|
742
|
|
- settings.settings = this._fixupSettingsList(response.settings);
|
|
707
|
+ settings.settings = this.#fixupSettingsList(response.settings);
|
743
|
708
|
}
|
744
|
709
|
if ("country" in response) {
|
745
|
710
|
settings.country = response.country;
|
... |
... |
@@ -753,7 +718,7 @@ class MoatRPC { |
753
|
718
|
// for
|
754
|
719
|
async circumvention_countries() {
|
755
|
720
|
const args = {};
|
756
|
|
- return this._makeRequest("circumvention/countries", args);
|
|
721
|
+ return this.#makeRequest("circumvention/countries", args);
|
757
|
722
|
}
|
758
|
723
|
|
759
|
724
|
// Request a copy of the builtin bridges, takes the following parameters:
|
... |
... |
@@ -766,7 +731,7 @@ class MoatRPC { |
766
|
731
|
const args = {
|
767
|
732
|
transports: transports ? transports : [],
|
768
|
733
|
};
|
769
|
|
- const response = await this._makeRequest("circumvention/builtin", args);
|
|
734
|
+ const response = await this.#makeRequest("circumvention/builtin", args);
|
770
|
735
|
if ("errors" in response) {
|
771
|
736
|
const code = response.errors[0].code;
|
772
|
737
|
const detail = response.errors[0].detail;
|
... |
... |
@@ -791,13 +756,13 @@ class MoatRPC { |
791
|
756
|
const args = {
|
792
|
757
|
transports: transports ? transports : [],
|
793
|
758
|
};
|
794
|
|
- const response = await this._makeRequest("circumvention/defaults", args);
|
|
759
|
+ const response = await this.#makeRequest("circumvention/defaults", args);
|
795
|
760
|
if ("errors" in response) {
|
796
|
761
|
const code = response.errors[0].code;
|
797
|
762
|
const detail = response.errors[0].detail;
|
798
|
763
|
throw new Error(`MoatRPC: ${detail} (${code})`);
|
799
|
764
|
} else if ("settings" in response) {
|
800
|
|
- return this._fixupSettingsList(response.settings);
|
|
765
|
+ return this.#fixupSettingsList(response.settings);
|
801
|
766
|
}
|
802
|
767
|
return [];
|
803
|
768
|
}
|
browser/modules/TorConnect.jsm
→
browser/modules/TorConnect.sys.mjs
1
|
|
-"use strict";
|
|
1
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2
|
4
|
|
3
|
|
-var EXPORTED_SYMBOLS = [
|
4
|
|
- "InternetStatus",
|
5
|
|
- "TorConnect",
|
6
|
|
- "TorConnectTopics",
|
7
|
|
- "TorConnectState",
|
8
|
|
-];
|
|
5
|
+import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
9
|
6
|
|
10
|
|
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
7
|
+const lazy = {};
|
11
|
8
|
|
12
|
|
-const { setTimeout, clearTimeout } = ChromeUtils.import(
|
13
|
|
- "resource://gre/modules/Timer.jsm"
|
14
|
|
-);
|
|
9
|
+ChromeUtils.defineESModuleGetters(lazy, {
|
|
10
|
+ MoatRPC: "resource:///modules/Moat.sys.mjs",
|
|
11
|
+ TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
|
|
12
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
13
|
+});
|
15
|
14
|
|
16
|
|
-const { BrowserWindowTracker } = ChromeUtils.import(
|
|
15
|
+// TODO: Should we move this to the about:torconnect actor?
|
|
16
|
+ChromeUtils.defineModuleGetter(
|
|
17
|
+ lazy,
|
|
18
|
+ "BrowserWindowTracker",
|
17
|
19
|
"resource:///modules/BrowserWindowTracker.jsm"
|
18
|
20
|
);
|
19
|
21
|
|
20
|
|
-const { TorMonitorService } = ChromeUtils.import(
|
21
|
|
- "resource://gre/modules/TorMonitorService.jsm"
|
22
|
|
-);
|
23
|
|
-const { TorBootstrapRequest } = ChromeUtils.import(
|
24
|
|
- "resource://gre/modules/TorBootstrapRequest.jsm"
|
25
|
|
-);
|
26
|
|
-
|
27
|
|
-const { TorSettings, TorSettingsTopics, TorBuiltinBridgeTypes } =
|
28
|
|
- ChromeUtils.import("resource:///modules/TorSettings.jsm");
|
|
22
|
+import {
|
|
23
|
+ TorSettings,
|
|
24
|
+ TorSettingsTopics,
|
|
25
|
+ TorBuiltinBridgeTypes,
|
|
26
|
+} from "resource:///modules/TorSettings.sys.mjs";
|
29
|
27
|
|
30
|
28
|
const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
|
31
|
29
|
|
32
|
|
-const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
|
33
|
|
-
|
34
|
30
|
const TorTopics = Object.freeze({
|
35
|
31
|
LogHasWarnOrErr: "TorLogHasWarnOrErr",
|
36
|
32
|
ProcessExited: "TorProcessExited",
|
... |
... |
@@ -46,7 +42,7 @@ const TorConnectPrefs = Object.freeze({ |
46
|
42
|
allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
|
47
|
43
|
});
|
48
|
44
|
|
49
|
|
-const TorConnectState = Object.freeze({
|
|
45
|
+export const TorConnectState = Object.freeze({
|
50
|
46
|
/* Our initial state */
|
51
|
47
|
Initial: "Initial",
|
52
|
48
|
/* In-between initial boot and bootstrapping, users can change tor network settings during this state */
|
... |
... |
@@ -156,7 +152,7 @@ const TorConnectStateTransitions = Object.freeze( |
156
|
152
|
);
|
157
|
153
|
|
158
|
154
|
/* Topics Notified by the TorConnect module */
|
159
|
|
-const TorConnectTopics = Object.freeze({
|
|
155
|
+export const TorConnectTopics = Object.freeze({
|
160
|
156
|
StateChange: "torconnect:state-change",
|
161
|
157
|
BootstrapProgress: "torconnect:bootstrap-progress",
|
162
|
158
|
BootstrapComplete: "torconnect:bootstrap-complete",
|
... |
... |
@@ -238,7 +234,7 @@ const debug_sleep = async ms => { |
238
|
234
|
});
|
239
|
235
|
};
|
240
|
236
|
|
241
|
|
-const InternetStatus = Object.freeze({
|
|
237
|
+export const InternetStatus = Object.freeze({
|
242
|
238
|
Unknown: -1,
|
243
|
239
|
Offline: 0,
|
244
|
240
|
Online: 1,
|
... |
... |
@@ -302,7 +298,7 @@ class InternetTest { |
302
|
298
|
// waiting both for the bootstrap, and for the Internet test.
|
303
|
299
|
// However, managing Moat with async/await is much easier as it avoids a
|
304
|
300
|
// callback hell, and it makes extra explicit that we are uniniting it.
|
305
|
|
- const mrpc = new MoatRPC();
|
|
301
|
+ const mrpc = new lazy.MoatRPC();
|
306
|
302
|
let status = null;
|
307
|
303
|
let error = null;
|
308
|
304
|
try {
|
... |
... |
@@ -340,7 +336,7 @@ class InternetTest { |
340
|
336
|
}
|
341
|
337
|
}
|
342
|
338
|
|
343
|
|
-const TorConnect = (() => {
|
|
339
|
+export const TorConnect = (() => {
|
344
|
340
|
let retval = {
|
345
|
341
|
_state: TorConnectState.Initial,
|
346
|
342
|
_bootstrapProgress: 0,
|
... |
... |
@@ -459,7 +455,7 @@ const TorConnect = (() => { |
459
|
455
|
return;
|
460
|
456
|
}
|
461
|
457
|
|
462
|
|
- const tbr = new TorBootstrapRequest();
|
|
458
|
+ const tbr = new lazy.TorBootstrapRequest();
|
463
|
459
|
const internetTest = new InternetTest();
|
464
|
460
|
let cancelled = false;
|
465
|
461
|
|
... |
... |
@@ -604,7 +600,7 @@ const TorConnect = (() => { |
604
|
600
|
|
605
|
601
|
// lookup user's potential censorship circumvention settings from Moat service
|
606
|
602
|
try {
|
607
|
|
- this.mrpc = new MoatRPC();
|
|
603
|
+ this.mrpc = new lazy.MoatRPC();
|
608
|
604
|
await this.mrpc.init();
|
609
|
605
|
|
610
|
606
|
if (this.transitioning) {
|
... |
... |
@@ -678,7 +674,7 @@ const TorConnect = (() => { |
678
|
674
|
await TorSettings.applySettings();
|
679
|
675
|
|
680
|
676
|
// build out our bootstrap request
|
681
|
|
- const tbr = new TorBootstrapRequest();
|
|
677
|
+ const tbr = new lazy.TorBootstrapRequest();
|
682
|
678
|
tbr.onbootstrapstatus = (progress, status) => {
|
683
|
679
|
TorConnect._updateBootstrapStatus(progress, status);
|
684
|
680
|
};
|
... |
... |
@@ -915,7 +911,7 @@ const TorConnect = (() => { |
915
|
911
|
* @type {boolean}
|
916
|
912
|
*/
|
917
|
913
|
get enabled() {
|
918
|
|
- return TorMonitorService.ownsTorDaemon;
|
|
914
|
+ return lazy.TorProviderBuilder.build().ownsTorDaemon;
|
919
|
915
|
},
|
920
|
916
|
|
921
|
917
|
get shouldShowTorConnect() {
|
... |
... |
@@ -1053,7 +1049,7 @@ const TorConnect = (() => { |
1053
|
1049
|
Further external commands and helper methods
|
1054
|
1050
|
*/
|
1055
|
1051
|
openTorPreferences() {
|
1056
|
|
- const win = BrowserWindowTracker.getTopWindow();
|
|
1052
|
+ const win = lazy.BrowserWindowTracker.getTopWindow();
|
1057
|
1053
|
win.switchToTabHavingURI("about:preferences#connection", true);
|
1058
|
1054
|
},
|
1059
|
1055
|
|
... |
... |
@@ -1073,7 +1069,7 @@ const TorConnect = (() => { |
1073
|
1069
|
* begin AutoBootstrapping, if possible.
|
1074
|
1070
|
*/
|
1075
|
1071
|
openTorConnect(options) {
|
1076
|
|
- const win = BrowserWindowTracker.getTopWindow();
|
|
1072
|
+ const win = lazy.BrowserWindowTracker.getTopWindow();
|
1077
|
1073
|
win.switchToTabHavingURI("about:torconnect", true, {
|
1078
|
1074
|
ignoreQueryString: true,
|
1079
|
1075
|
});
|
... |
... |
@@ -1094,7 +1090,7 @@ const TorConnect = (() => { |
1094
|
1090
|
},
|
1095
|
1091
|
|
1096
|
1092
|
viewTorLogs() {
|
1097
|
|
- const win = BrowserWindowTracker.getTopWindow();
|
|
1093
|
+ const win = lazy.BrowserWindowTracker.getTopWindow();
|
1098
|
1094
|
win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
|
1099
|
1095
|
},
|
1100
|
1096
|
|
... |
... |
@@ -1104,7 +1100,7 @@ const TorConnect = (() => { |
1104
|
1100
|
if (this._countryCodes.length) {
|
1105
|
1101
|
return this._countryCodes;
|
1106
|
1102
|
}
|
1107
|
|
- const mrpc = new MoatRPC();
|
|
1103
|
+ const mrpc = new lazy.MoatRPC();
|
1108
|
1104
|
try {
|
1109
|
1105
|
await mrpc.init();
|
1110
|
1106
|
this._countryCodes = await mrpc.circumvention_countries();
|
browser/modules/TorSettings.jsm
→
browser/modules/TorSettings.sys.mjs
1
|
|
-"use strict";
|
|
1
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2
|
4
|
|
3
|
|
-var EXPORTED_SYMBOLS = [
|
4
|
|
- "TorSettings",
|
5
|
|
- "TorSettingsTopics",
|
6
|
|
- "TorSettingsData",
|
7
|
|
- "TorBridgeSource",
|
8
|
|
- "TorBuiltinBridgeTypes",
|
9
|
|
- "TorProxyType",
|
10
|
|
-];
|
|
5
|
+const lazy = {};
|
11
|
6
|
|
12
|
|
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
13
|
|
-
|
14
|
|
-const { TorMonitorService } = ChromeUtils.import(
|
15
|
|
- "resource://gre/modules/TorMonitorService.jsm"
|
16
|
|
-);
|
17
|
|
-const { TorProtocolService } = ChromeUtils.import(
|
18
|
|
- "resource://gre/modules/TorProtocolService.jsm"
|
19
|
|
-);
|
20
|
|
-
|
21
|
|
-/* tor-launcher observer topics */
|
22
|
|
-const TorTopics = Object.freeze({
|
23
|
|
- ProcessIsReady: "TorProcessIsReady",
|
|
7
|
+ChromeUtils.defineESModuleGetters(lazy, {
|
|
8
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
9
|
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
24
|
10
|
});
|
25
|
11
|
|
26
|
12
|
/* TorSettings observer topics */
|
27
|
|
-const TorSettingsTopics = Object.freeze({
|
|
13
|
+export const TorSettingsTopics = Object.freeze({
|
28
|
14
|
Ready: "torsettings:ready",
|
29
|
15
|
SettingChanged: "torsettings:setting-changed",
|
30
|
16
|
});
|
31
|
17
|
|
32
|
18
|
/* TorSettings observer data (for SettingChanged topic) */
|
33
|
|
-const TorSettingsData = Object.freeze({
|
|
19
|
+export const TorSettingsData = Object.freeze({
|
34
|
20
|
QuickStartEnabled: "torsettings:quickstart_enabled",
|
35
|
21
|
});
|
36
|
22
|
|
... |
... |
@@ -98,21 +84,21 @@ const TorConfigKeys = Object.freeze({ |
98
|
84
|
clientTransportPlugin: "ClientTransportPlugin",
|
99
|
85
|
});
|
100
|
86
|
|
101
|
|
-const TorBridgeSource = Object.freeze({
|
|
87
|
+export const TorBridgeSource = Object.freeze({
|
102
|
88
|
Invalid: -1,
|
103
|
89
|
BuiltIn: 0,
|
104
|
90
|
BridgeDB: 1,
|
105
|
91
|
UserProvided: 2,
|
106
|
92
|
});
|
107
|
93
|
|
108
|
|
-const TorProxyType = Object.freeze({
|
|
94
|
+export const TorProxyType = Object.freeze({
|
109
|
95
|
Invalid: -1,
|
110
|
96
|
Socks4: 0,
|
111
|
97
|
Socks5: 1,
|
112
|
98
|
HTTPS: 2,
|
113
|
99
|
});
|
114
|
100
|
|
115
|
|
-const TorBuiltinBridgeTypes = Object.freeze(
|
|
101
|
+export const TorBuiltinBridgeTypes = Object.freeze(
|
116
|
102
|
(() => {
|
117
|
103
|
const bridgeListBranch = Services.prefs.getBranch(
|
118
|
104
|
TorLauncherPrefs.default_bridge
|
... |
... |
@@ -254,7 +240,7 @@ const arrayCopy = function (array) { |
254
|
240
|
|
255
|
241
|
/* TorSettings module */
|
256
|
242
|
|
257
|
|
-const TorSettings = (() => {
|
|
243
|
+export const TorSettings = (() => {
|
258
|
244
|
const self = {
|
259
|
245
|
_settings: null,
|
260
|
246
|
|
... |
... |
@@ -288,7 +274,8 @@ const TorSettings = (() => { |
288
|
274
|
|
289
|
275
|
/* load or init our settings, and register observers */
|
290
|
276
|
init() {
|
291
|
|
- if (TorMonitorService.ownsTorDaemon) {
|
|
277
|
+ const provider = lazy.TorProviderBuilder.build();
|
|
278
|
+ if (provider.ownsTorDaemon) {
|
292
|
279
|
// if the settings branch exists, load settings from prefs
|
293
|
280
|
if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
|
294
|
281
|
this.loadFromPrefs();
|
... |
... |
@@ -296,9 +283,9 @@ const TorSettings = (() => { |
296
|
283
|
// otherwise load defaults
|
297
|
284
|
this._settings = this.defaultSettings();
|
298
|
285
|
}
|
299
|
|
- Services.obs.addObserver(this, TorTopics.ProcessIsReady);
|
|
286
|
+ Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
|
300
|
287
|
|
301
|
|
- if (TorMonitorService.isRunning) {
|
|
288
|
+ if (provider.isRunning) {
|
302
|
289
|
this.handleProcessReady();
|
303
|
290
|
}
|
304
|
291
|
}
|
... |
... |
@@ -309,8 +296,11 @@ const TorSettings = (() => { |
309
|
296
|
console.log(`TorSettings: Observed ${topic}`);
|
310
|
297
|
|
311
|
298
|
switch (topic) {
|
312
|
|
- case TorTopics.ProcessIsReady:
|
313
|
|
- Services.obs.removeObserver(this, TorTopics.ProcessIsReady);
|
|
299
|
+ case lazy.TorProviderTopics.ProcessIsReady:
|
|
300
|
+ Services.obs.removeObserver(
|
|
301
|
+ this,
|
|
302
|
+ lazy.TorProviderTopics.ProcessIsReady
|
|
303
|
+ );
|
314
|
304
|
await this.handleProcessReady();
|
315
|
305
|
break;
|
316
|
306
|
}
|
... |
... |
@@ -569,7 +559,7 @@ const TorSettings = (() => { |
569
|
559
|
}
|
570
|
560
|
|
571
|
561
|
/* Push to Tor */
|
572
|
|
- await TorProtocolService.writeSettings(settingsMap);
|
|
562
|
+ await lazy.TorProviderBuilder.build().writeSettings(settingsMap);
|
573
|
563
|
|
574
|
564
|
return this;
|
575
|
565
|
},
|
browser/modules/moz.build
... |
... |
@@ -123,7 +123,7 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"] |
123
|
123
|
EXTRA_JS_MODULES += [
|
124
|
124
|
"AboutNewTab.jsm",
|
125
|
125
|
"AsyncTabSwitcher.jsm",
|
126
|
|
- "BridgeDB.jsm",
|
|
126
|
+ "BridgeDB.sys.mjs",
|
127
|
127
|
"BrowserUIUtils.jsm",
|
128
|
128
|
"BrowserUsageTelemetry.jsm",
|
129
|
129
|
"BrowserWindowTracker.jsm",
|
... |
... |
@@ -135,7 +135,7 @@ EXTRA_JS_MODULES += [ |
135
|
135
|
"FeatureCallout.sys.mjs",
|
136
|
136
|
"HomePage.jsm",
|
137
|
137
|
"LaterRun.jsm",
|
138
|
|
- 'Moat.jsm',
|
|
138
|
+ "Moat.sys.mjs",
|
139
|
139
|
"NewTabPagePreloading.jsm",
|
140
|
140
|
"OpenInTabsUtils.jsm",
|
141
|
141
|
"PageActions.jsm",
|
... |
... |
@@ -149,8 +149,8 @@ EXTRA_JS_MODULES += [ |
149
|
149
|
"SitePermissions.sys.mjs",
|
150
|
150
|
"TabsList.jsm",
|
151
|
151
|
"TabUnloader.jsm",
|
152
|
|
- "TorConnect.jsm",
|
153
|
|
- "TorSettings.jsm",
|
|
152
|
+ "TorConnect.sys.mjs",
|
|
153
|
+ "TorSettings.sys.mjs",
|
154
|
154
|
"TorStrings.jsm",
|
155
|
155
|
"TransientPrefs.jsm",
|
156
|
156
|
"URILoadingHelper.sys.mjs",
|
toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
1
|
1
|
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
2
|
2
|
|
3
|
|
-import { TorProtocolService } from "resource://gre/modules/TorProtocolService.sys.mjs";
|
|
3
|
+import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
|
4
|
4
|
import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
|
5
|
5
|
|
6
|
6
|
/* tor-launcher observer topics */
|
... |
... |
@@ -13,19 +13,23 @@ export const TorTopics = Object.freeze({ |
13
|
13
|
// modeled after XMLHttpRequest
|
14
|
14
|
// nicely encapsulates the observer register/unregister logic
|
15
|
15
|
export class TorBootstrapRequest {
|
|
16
|
+ // number of ms to wait before we abandon the bootstrap attempt
|
|
17
|
+ // a value of 0 implies we never wait
|
|
18
|
+ timeout = 0;
|
|
19
|
+
|
|
20
|
+ // callbacks for bootstrap process status updates
|
|
21
|
+ onbootstrapstatus = (progress, status) => {};
|
|
22
|
+ onbootstrapcomplete = () => {};
|
|
23
|
+ onbootstraperror = (message, details) => {};
|
|
24
|
+
|
|
25
|
+ // internal resolve() method for bootstrap
|
|
26
|
+ #bootstrapPromiseResolve = null;
|
|
27
|
+ #bootstrapPromise = null;
|
|
28
|
+ #timeoutID = null;
|
|
29
|
+ #provider = null;
|
|
30
|
+
|
16
|
31
|
constructor() {
|
17
|
|
- // number of ms to wait before we abandon the bootstrap attempt
|
18
|
|
- // a value of 0 implies we never wait
|
19
|
|
- this.timeout = 0;
|
20
|
|
- // callbacks for bootstrap process status updates
|
21
|
|
- this.onbootstrapstatus = (progress, status) => {};
|
22
|
|
- this.onbootstrapcomplete = () => {};
|
23
|
|
- this.onbootstraperror = (message, details) => {};
|
24
|
|
-
|
25
|
|
- // internal resolve() method for bootstrap
|
26
|
|
- this._bootstrapPromiseResolve = null;
|
27
|
|
- this._bootstrapPromise = null;
|
28
|
|
- this._timeoutID = null;
|
|
32
|
+ this.#provider = TorProviderBuilder.build();
|
29
|
33
|
}
|
30
|
34
|
|
31
|
35
|
observe(subject, topic, data) {
|
... |
... |
@@ -41,15 +45,16 @@ export class TorBootstrapRequest { |
41
|
45
|
if (this.onbootstrapcomplete) {
|
42
|
46
|
this.onbootstrapcomplete();
|
43
|
47
|
}
|
44
|
|
- this._bootstrapPromiseResolve(true);
|
45
|
|
- clearTimeout(this._timeoutID);
|
|
48
|
+ this.#bootstrapPromiseResolve(true);
|
|
49
|
+ clearTimeout(this.#timeoutID);
|
|
50
|
+ this.#timeoutID = null;
|
46
|
51
|
}
|
47
|
52
|
|
48
|
53
|
break;
|
49
|
54
|
}
|
50
|
55
|
case TorTopics.BootstrapError: {
|
51
|
56
|
console.info("TorBootstrapRequest: observerd TorBootstrapError", obj);
|
52
|
|
- this._stop(obj?.message, obj?.details);
|
|
57
|
+ this.#stop(obj?.message, obj?.details);
|
53
|
58
|
break;
|
54
|
59
|
}
|
55
|
60
|
}
|
... |
... |
@@ -57,12 +62,12 @@ export class TorBootstrapRequest { |
57
|
62
|
|
58
|
63
|
// resolves 'true' if bootstrap succeeds, false otherwise
|
59
|
64
|
bootstrap() {
|
60
|
|
- if (this._bootstrapPromise) {
|
61
|
|
- return this._bootstrapPromise;
|
|
65
|
+ if (this.#bootstrapPromise) {
|
|
66
|
+ return this.#bootstrapPromise;
|
62
|
67
|
}
|
63
|
68
|
|
64
|
|
- this._bootstrapPromise = new Promise((resolve, reject) => {
|
65
|
|
- this._bootstrapPromiseResolve = resolve;
|
|
69
|
+ this.#bootstrapPromise = new Promise((resolve, reject) => {
|
|
70
|
+ this.#bootstrapPromiseResolve = resolve;
|
66
|
71
|
|
67
|
72
|
// register ourselves to listen for bootstrap events
|
68
|
73
|
Services.obs.addObserver(this, TorTopics.BootstrapStatus);
|
... |
... |
@@ -70,10 +75,10 @@ export class TorBootstrapRequest { |
70
|
75
|
|
71
|
76
|
// optionally cancel bootstrap after a given timeout
|
72
|
77
|
if (this.timeout > 0) {
|
73
|
|
- this._timeoutID = setTimeout(async () => {
|
74
|
|
- this._timeoutID = null;
|
|
78
|
+ this.#timeoutID = setTimeout(async () => {
|
|
79
|
+ this.#timeoutID = null;
|
75
|
80
|
// TODO: Translate, if really used
|
76
|
|
- await this._stop(
|
|
81
|
+ await this.#stop(
|
77
|
82
|
"Tor Bootstrap process timed out",
|
78
|
83
|
`Bootstrap attempt abandoned after waiting ${this.timeout} ms`
|
79
|
84
|
);
|
... |
... |
@@ -81,38 +86,45 @@ export class TorBootstrapRequest { |
81
|
86
|
}
|
82
|
87
|
|
83
|
88
|
// wait for bootstrapping to begin and maybe handle error
|
84
|
|
- TorProtocolService.connect().catch(err => {
|
85
|
|
- this._stop(err.message, "");
|
|
89
|
+ this.#provider.connect().catch(err => {
|
|
90
|
+ this.#stop(err.message, "");
|
86
|
91
|
});
|
87
|
92
|
}).finally(() => {
|
88
|
93
|
// and remove ourselves once bootstrap is resolved
|
89
|
94
|
Services.obs.removeObserver(this, TorTopics.BootstrapStatus);
|
90
|
95
|
Services.obs.removeObserver(this, TorTopics.BootstrapError);
|
91
|
|
- this._bootstrapPromise = null;
|
|
96
|
+ this.#bootstrapPromise = null;
|
92
|
97
|
});
|
93
|
98
|
|
94
|
|
- return this._bootstrapPromise;
|
|
99
|
+ return this.#bootstrapPromise;
|
95
|
100
|
}
|
96
|
101
|
|
97
|
102
|
async cancel() {
|
98
|
|
- await this._stop();
|
|
103
|
+ await this.#stop();
|
99
|
104
|
}
|
100
|
105
|
|
101
|
106
|
// Internal implementation. Do not use directly, but call cancel, instead.
|
102
|
|
- async _stop(message, details) {
|
|
107
|
+ async #stop(message, details) {
|
103
|
108
|
// first stop our bootstrap timeout before handling the error
|
104
|
|
- if (this._timeoutID !== null) {
|
105
|
|
- clearTimeout(this._timeoutID);
|
106
|
|
- this._timeoutID = null;
|
|
109
|
+ if (this.#timeoutID !== null) {
|
|
110
|
+ clearTimeout(this.#timeoutID);
|
|
111
|
+ this.#timeoutID = null;
|
107
|
112
|
}
|
108
|
113
|
|
109
|
|
- // stopBootstrap never throws
|
110
|
|
- await TorProtocolService.stopBootstrap();
|
|
114
|
+ try {
|
|
115
|
+ await this.#provider.stopBootstrap();
|
|
116
|
+ } catch (e) {
|
|
117
|
+ console.error("Failed to stop the bootstrap.", e);
|
|
118
|
+ if (!message) {
|
|
119
|
+ message = e.message;
|
|
120
|
+ details = "";
|
|
121
|
+ }
|
|
122
|
+ }
|
111
|
123
|
|
112
|
124
|
if (this.onbootstraperror && message) {
|
113
|
125
|
this.onbootstraperror(message, details);
|
114
|
126
|
}
|
115
|
127
|
|
116
|
|
- this._bootstrapPromiseResolve(false);
|
|
128
|
+ this.#bootstrapPromiseResolve(false);
|
117
|
129
|
}
|
118
|
130
|
} |
toolkit/components/tor-launcher/TorControlPort.sys.mjs
... |
... |
@@ -274,6 +274,44 @@ class AsyncSocket { |
274
|
274
|
* the command
|
275
|
275
|
*/
|
276
|
276
|
|
|
277
|
+/**
|
|
278
|
+ * @typedef {object} Bridge
|
|
279
|
+ * @property {string} transport The transport of the bridge, or vanilla if not
|
|
280
|
+ * specified.
|
|
281
|
+ * @property {string} addr The IP address and port of the bridge
|
|
282
|
+ * @property {string} id The fingerprint of the bridge
|
|
283
|
+ * @property {string} args Optional arguments passed to the bridge
|
|
284
|
+ */
|
|
285
|
+/**
|
|
286
|
+ * @typedef {object} PTInfo The information about a pluggable transport
|
|
287
|
+ * @property {string[]} transports An array with all the transports supported by
|
|
288
|
+ * this configuration.
|
|
289
|
+ * @property {string} type Either socks4, socks5 or exec
|
|
290
|
+ * @property {string} [ip] The IP address of the proxy (only for socks4 and
|
|
291
|
+ * socks5)
|
|
292
|
+ * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
|
|
293
|
+ * @property {string} [pathToBinary] Path to the binary that is run (only for
|
|
294
|
+ * exec)
|
|
295
|
+ * @property {string} [options] Optional options passed to the binary (only for
|
|
296
|
+ * exec)
|
|
297
|
+ */
|
|
298
|
+/**
|
|
299
|
+ * @typedef {object} OnionAuthKeyInfo
|
|
300
|
+ * @property {string} address The address of the onion service
|
|
301
|
+ * @property {string} typeAndKey Onion service key and type of key, as
|
|
302
|
+ * `type:base64-private-key`
|
|
303
|
+ * @property {string} Flags Additional flags, such as Permanent
|
|
304
|
+ */
|
|
305
|
+/**
|
|
306
|
+ * @callback EventFilterCallback
|
|
307
|
+ * @param {any} data Either a raw string, or already parsed data
|
|
308
|
+ * @returns {boolean}
|
|
309
|
+ */
|
|
310
|
+/**
|
|
311
|
+ * @callback EventCallback
|
|
312
|
+ * @param {any} data Either a raw string, or already parsed data
|
|
313
|
+ */
|
|
314
|
+
|
277
|
315
|
class TorError extends Error {
|
278
|
316
|
constructor(command, reply) {
|
279
|
317
|
super(`${command} -> ${reply}`);
|
... |
... |
@@ -584,319 +622,6 @@ class ControlSocket { |
584
|
622
|
}
|
585
|
623
|
}
|
586
|
624
|
|
587
|
|
-// ## utils
|
588
|
|
-// A namespace for utility functions
|
589
|
|
-let utils = {};
|
590
|
|
-
|
591
|
|
-// __utils.identity(x)__.
|
592
|
|
-// Returns its argument unchanged.
|
593
|
|
-utils.identity = function (x) {
|
594
|
|
- return x;
|
595
|
|
-};
|
596
|
|
-
|
597
|
|
-// __utils.capture(string, regex)__.
|
598
|
|
-// Takes a string and returns an array of capture items, where regex must have a single
|
599
|
|
-// capturing group and use the suffix /.../g to specify a global search.
|
600
|
|
-utils.capture = function (string, regex) {
|
601
|
|
- let matches = [];
|
602
|
|
- // Special trick to use string.replace for capturing multiple matches.
|
603
|
|
- string.replace(regex, function (a, captured) {
|
604
|
|
- matches.push(captured);
|
605
|
|
- });
|
606
|
|
- return matches;
|
607
|
|
-};
|
608
|
|
-
|
609
|
|
-// __utils.extractor(regex)__.
|
610
|
|
-// Returns a function that takes a string and returns an array of regex matches. The
|
611
|
|
-// regex must use the suffix /.../g to specify a global search.
|
612
|
|
-utils.extractor = function (regex) {
|
613
|
|
- return function (text) {
|
614
|
|
- return utils.capture(text, regex);
|
615
|
|
- };
|
616
|
|
-};
|
617
|
|
-
|
618
|
|
-// __utils.splitLines(string)__.
|
619
|
|
-// Splits a string into an array of strings, each corresponding to a line.
|
620
|
|
-utils.splitLines = function (string) {
|
621
|
|
- return string.split(/\r?\n/);
|
622
|
|
-};
|
623
|
|
-
|
624
|
|
-// __utils.splitAtSpaces(string)__.
|
625
|
|
-// Splits a string into chunks between spaces. Does not split at spaces
|
626
|
|
-// inside pairs of quotation marks.
|
627
|
|
-utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
|
628
|
|
-
|
629
|
|
-// __utils.splitAtFirst(string, regex)__.
|
630
|
|
-// Splits a string at the first instance of regex match. If no match is
|
631
|
|
-// found, returns the whole string.
|
632
|
|
-utils.splitAtFirst = function (string, regex) {
|
633
|
|
- let match = string.match(regex);
|
634
|
|
- return match
|
635
|
|
- ? [
|
636
|
|
- string.substring(0, match.index),
|
637
|
|
- string.substring(match.index + match[0].length),
|
638
|
|
- ]
|
639
|
|
- : string;
|
640
|
|
-};
|
641
|
|
-
|
642
|
|
-// __utils.splitAtEquals(string)__.
|
643
|
|
-// Splits a string into chunks between equals. Does not split at equals
|
644
|
|
-// inside pairs of quotation marks.
|
645
|
|
-utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
|
646
|
|
-
|
647
|
|
-// __utils.mergeObjects(arrayOfObjects)__.
|
648
|
|
-// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
|
649
|
|
-// Pure function.
|
650
|
|
-utils.mergeObjects = function (arrayOfObjects) {
|
651
|
|
- let result = {};
|
652
|
|
- for (let obj of arrayOfObjects) {
|
653
|
|
- for (let key in obj) {
|
654
|
|
- result[key] = obj[key];
|
655
|
|
- }
|
656
|
|
- }
|
657
|
|
- return result;
|
658
|
|
-};
|
659
|
|
-
|
660
|
|
-// __utils.listMapData(parameterString, listNames)__.
|
661
|
|
-// Takes a list of parameters separated by spaces, of which the first several are
|
662
|
|
-// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
|
663
|
|
-// to the unnamed parameters, and combine them in a map with the named parameters.
|
664
|
|
-// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
|
665
|
|
-//
|
666
|
|
-// utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
|
667
|
|
-// ["streamID", "event", "circuitID", "IP"])
|
668
|
|
-// // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
|
669
|
|
-// // "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
|
670
|
|
-utils.listMapData = function (parameterString, listNames) {
|
671
|
|
- // Split out the space-delimited parameters.
|
672
|
|
- let parameters = utils.splitAtSpaces(parameterString),
|
673
|
|
- dataMap = {};
|
674
|
|
- // Assign listNames to the first n = listNames.length parameters.
|
675
|
|
- for (let i = 0; i < listNames.length; ++i) {
|
676
|
|
- dataMap[listNames[i]] = parameters[i];
|
677
|
|
- }
|
678
|
|
- // Read key-value pairs and copy these to the dataMap.
|
679
|
|
- for (let i = listNames.length; i < parameters.length; ++i) {
|
680
|
|
- let [key, value] = utils.splitAtEquals(parameters[i]);
|
681
|
|
- if (key && value) {
|
682
|
|
- dataMap[key] = value;
|
683
|
|
- }
|
684
|
|
- }
|
685
|
|
- return dataMap;
|
686
|
|
-};
|
687
|
|
-
|
688
|
|
-// ## info
|
689
|
|
-// A namespace for functions related to tor's GETINFO and GETCONF command.
|
690
|
|
-let info = {};
|
691
|
|
-
|
692
|
|
-// __info.keyValueStringsFromMessage(messageText)__.
|
693
|
|
-// Takes a message (text) response to GETINFO or GETCONF and provides
|
694
|
|
-// a series of key-value strings, which are either multiline (with a `250+` prefix):
|
695
|
|
-//
|
696
|
|
-// 250+config/defaults=
|
697
|
|
-// AccountingMax "0 bytes"
|
698
|
|
-// AllowDotExit "0"
|
699
|
|
-// .
|
700
|
|
-//
|
701
|
|
-// or single-line (with a `250-` or `250 ` prefix):
|
702
|
|
-//
|
703
|
|
-// 250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
|
704
|
|
-info.keyValueStringsFromMessage = utils.extractor(
|
705
|
|
- /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
|
706
|
|
-);
|
707
|
|
-
|
708
|
|
-// __info.applyPerLine(transformFunction)__.
|
709
|
|
-// Returns a function that splits text into lines,
|
710
|
|
-// and applies transformFunction to each line.
|
711
|
|
-info.applyPerLine = function (transformFunction) {
|
712
|
|
- return function (text) {
|
713
|
|
- return utils.splitLines(text.trim()).map(transformFunction);
|
714
|
|
- };
|
715
|
|
-};
|
716
|
|
-
|
717
|
|
-// __info.routerStatusParser(valueString)__.
|
718
|
|
-// Parses a router status entry as, described in
|
719
|
|
-// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
|
720
|
|
-// (search for "router status entry")
|
721
|
|
-info.routerStatusParser = function (valueString) {
|
722
|
|
- let lines = utils.splitLines(valueString),
|
723
|
|
- objects = [];
|
724
|
|
- for (let line of lines) {
|
725
|
|
- // Drop first character and grab data following it.
|
726
|
|
- let myData = line.substring(2),
|
727
|
|
- // Accumulate more maps with data, depending on the first character in the line.
|
728
|
|
- dataFun = {
|
729
|
|
- r: data =>
|
730
|
|
- utils.listMapData(data, [
|
731
|
|
- "nickname",
|
732
|
|
- "identity",
|
733
|
|
- "digest",
|
734
|
|
- "publicationDate",
|
735
|
|
- "publicationTime",
|
736
|
|
- "IP",
|
737
|
|
- "ORPort",
|
738
|
|
- "DirPort",
|
739
|
|
- ]),
|
740
|
|
- a: data => ({ IPv6: data }),
|
741
|
|
- s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
|
742
|
|
- v: data => ({ version: data }),
|
743
|
|
- w: data => utils.listMapData(data, []),
|
744
|
|
- p: data => ({ portList: data.split(",") }),
|
745
|
|
- }[line.charAt(0)];
|
746
|
|
- if (dataFun !== undefined) {
|
747
|
|
- objects.push(dataFun(myData));
|
748
|
|
- }
|
749
|
|
- }
|
750
|
|
- return utils.mergeObjects(objects);
|
751
|
|
-};
|
752
|
|
-
|
753
|
|
-// __info.circuitStatusParser(line)__.
|
754
|
|
-// Parse the output of a circuit status line.
|
755
|
|
-info.circuitStatusParser = function (line) {
|
756
|
|
- let data = utils.listMapData(line, ["id", "status", "circuit"]),
|
757
|
|
- circuit = data.circuit;
|
758
|
|
- // Parse out the individual circuit IDs and names.
|
759
|
|
- if (circuit) {
|
760
|
|
- data.circuit = circuit.split(",").map(function (x) {
|
761
|
|
- return x.split(/~|=/);
|
762
|
|
- });
|
763
|
|
- }
|
764
|
|
- return data;
|
765
|
|
-};
|
766
|
|
-
|
767
|
|
-// __info.streamStatusParser(line)__.
|
768
|
|
-// Parse the output of a stream status line.
|
769
|
|
-info.streamStatusParser = function (text) {
|
770
|
|
- return utils.listMapData(text, [
|
771
|
|
- "StreamID",
|
772
|
|
- "StreamStatus",
|
773
|
|
- "CircuitID",
|
774
|
|
- "Target",
|
775
|
|
- ]);
|
776
|
|
-};
|
777
|
|
-
|
778
|
|
-// TODO: fix this parsing logic to handle bridgeLine correctly
|
779
|
|
-// fingerprint/id is an optional parameter
|
780
|
|
-// __info.bridgeParser(bridgeLine)__.
|
781
|
|
-// Takes a single line from a `getconf bridge` result and returns
|
782
|
|
-// a map containing the bridge's type, address, and ID.
|
783
|
|
-info.bridgeParser = function (bridgeLine) {
|
784
|
|
- let result = {},
|
785
|
|
- tokens = bridgeLine.split(/\s+/);
|
786
|
|
- // First check if we have a "vanilla" bridge:
|
787
|
|
- if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
|
788
|
|
- result.type = "vanilla";
|
789
|
|
- [result.address, result.ID] = tokens;
|
790
|
|
- // Several bridge types have a similar format:
|
791
|
|
- } else {
|
792
|
|
- result.type = tokens[0];
|
793
|
|
- if (
|
794
|
|
- [
|
795
|
|
- "flashproxy",
|
796
|
|
- "fte",
|
797
|
|
- "meek",
|
798
|
|
- "meek_lite",
|
799
|
|
- "obfs3",
|
800
|
|
- "obfs4",
|
801
|
|
- "scramblesuit",
|
802
|
|
- "snowflake",
|
803
|
|
- ].includes(result.type)
|
804
|
|
- ) {
|
805
|
|
- [result.address, result.ID] = tokens.slice(1);
|
806
|
|
- }
|
807
|
|
- }
|
808
|
|
- return result.type ? result : null;
|
809
|
|
-};
|
810
|
|
-
|
811
|
|
-// __info.parsers__.
|
812
|
|
-// A map of GETINFO and GETCONF keys to parsing function, which convert
|
813
|
|
-// result strings to _javascript_ data.
|
814
|
|
-info.parsers = {
|
815
|
|
- "ns/id/": info.routerStatusParser,
|
816
|
|
- "ip-to-country/": utils.identity,
|
817
|
|
- "circuit-status": info.applyPerLine(info.circuitStatusParser),
|
818
|
|
- bridge: info.bridgeParser,
|
819
|
|
- // Currently unused parsers:
|
820
|
|
- // "ns/name/" : info.routerStatusParser,
|
821
|
|
- // "stream-status" : info.applyPerLine(info.streamStatusParser),
|
822
|
|
- // "version" : utils.identity,
|
823
|
|
- // "config-file" : utils.identity,
|
824
|
|
-};
|
825
|
|
-
|
826
|
|
-// __info.getParser(key)__.
|
827
|
|
-// Takes a key and determines the parser function that should be used to
|
828
|
|
-// convert its corresponding valueString to _javascript_ data.
|
829
|
|
-info.getParser = function (key) {
|
830
|
|
- return (
|
831
|
|
- info.parsers[key] ||
|
832
|
|
- info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
|
833
|
|
- );
|
834
|
|
-};
|
835
|
|
-
|
836
|
|
-// __info.stringToValue(string)__.
|
837
|
|
-// Converts a key-value string as from GETINFO or GETCONF to a value.
|
838
|
|
-info.stringToValue = function (string) {
|
839
|
|
- // key should look something like `250+circuit-status=` or `250-circuit-status=...`
|
840
|
|
- // or `250 circuit-status=...`
|
841
|
|
- let matchForKey = string.match(/^250[ +-](.+?)=/),
|
842
|
|
- key = matchForKey ? matchForKey[1] : null;
|
843
|
|
- if (key === null) {
|
844
|
|
- return null;
|
845
|
|
- }
|
846
|
|
- // matchResult finds a single-line result for `250-` or `250 `,
|
847
|
|
- // or a multi-line one for `250+`.
|
848
|
|
- let matchResult =
|
849
|
|
- string.match(/^250[ -].+?=(.*)$/) ||
|
850
|
|
- string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
|
851
|
|
- // Retrieve the captured group (the text of the value in the key-value pair)
|
852
|
|
- valueString = matchResult ? matchResult[1] : null,
|
853
|
|
- // Get the parser function for the key found.
|
854
|
|
- parse = info.getParser(key.toLowerCase());
|
855
|
|
- if (parse === undefined) {
|
856
|
|
- throw new Error("No parser found for '" + key + "'");
|
857
|
|
- }
|
858
|
|
- // Return value produced by the parser.
|
859
|
|
- return parse(valueString);
|
860
|
|
-};
|
861
|
|
-
|
862
|
|
-/**
|
863
|
|
- * @typedef {object} Bridge
|
864
|
|
- * @property {string} transport The transport of the bridge, or vanilla if not
|
865
|
|
- * specified.
|
866
|
|
- * @property {string} addr The IP address and port of the bridge
|
867
|
|
- * @property {string} id The fingerprint of the bridge
|
868
|
|
- * @property {string} args Optional arguments passed to the bridge
|
869
|
|
- */
|
870
|
|
-/**
|
871
|
|
- * @typedef {object} PTInfo The information about a pluggable transport
|
872
|
|
- * @property {string[]} transports An array with all the transports supported by
|
873
|
|
- * this configuration.
|
874
|
|
- * @property {string} type Either socks4, socks5 or exec
|
875
|
|
- * @property {string} [ip] The IP address of the proxy (only for socks4 and
|
876
|
|
- * socks5)
|
877
|
|
- * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
|
878
|
|
- * @property {string} [pathToBinary] Path to the binary that is run (only for
|
879
|
|
- * exec)
|
880
|
|
- * @property {string} [options] Optional options passed to the binary (only for
|
881
|
|
- * exec)
|
882
|
|
- */
|
883
|
|
-/**
|
884
|
|
- * @typedef {object} OnionAuthKeyInfo
|
885
|
|
- * @property {string} address The address of the onion service
|
886
|
|
- * @property {string} typeAndKey Onion service key and type of key, as
|
887
|
|
- * `type:base64-private-key`
|
888
|
|
- * @property {string} Flags Additional flags, such as Permanent
|
889
|
|
- */
|
890
|
|
-/**
|
891
|
|
- * @callback EventFilterCallback
|
892
|
|
- * @param {any} data Either a raw string, or already parsed data
|
893
|
|
- * @returns {boolean}
|
894
|
|
- */
|
895
|
|
-/**
|
896
|
|
- * @callback EventCallback
|
897
|
|
- * @param {any} data Either a raw string, or already parsed data
|
898
|
|
- */
|
899
|
|
-
|
900
|
625
|
class TorController {
|
901
|
626
|
/**
|
902
|
627
|
* The control socket
|
... |
... |
@@ -905,16 +630,6 @@ class TorController { |
905
|
630
|
*/
|
906
|
631
|
#socket;
|
907
|
632
|
|
908
|
|
- /**
|
909
|
|
- * A map of EVENT keys to parsing functions, which convert result strings to
|
910
|
|
- * _javascript_ data.
|
911
|
|
- */
|
912
|
|
- #eventParsers = {
|
913
|
|
- stream: info.streamStatusParser,
|
914
|
|
- // Currently unused:
|
915
|
|
- // "circ" : info.circuitStatusParser,
|
916
|
|
- };
|
917
|
|
-
|
918
|
633
|
/**
|
919
|
634
|
* Builds a new TorController.
|
920
|
635
|
*
|
... |
... |
@@ -981,18 +696,6 @@ class TorController { |
981
|
696
|
await this.#sendCommandSimple(`authenticate ${password || ""}`);
|
982
|
697
|
}
|
983
|
698
|
|
984
|
|
- /**
|
985
|
|
- * Sends a GETINFO for a single key.
|
986
|
|
- *
|
987
|
|
- * @param {string} key The key to get value for
|
988
|
|
- * @returns {any} The return value depends on the requested key
|
989
|
|
- */
|
990
|
|
- async getInfo(key) {
|
991
|
|
- this.#expectString(key, "key");
|
992
|
|
- const response = await this.sendCommand(`getinfo ${key}`);
|
993
|
|
- return this.#getMultipleResponseValues(response)[0];
|
994
|
|
- }
|
995
|
|
-
|
996
|
699
|
/**
|
997
|
700
|
* Sends a GETINFO for a single key.
|
998
|
701
|
* control-spec.txt says "one ReplyLine is sent for each requested value", so,
|
... |
... |
@@ -1054,9 +757,7 @@ class TorController { |
1054
|
757
|
const addresses = [v4[5]];
|
1055
|
758
|
// a address:port
|
1056
|
759
|
// dir-spec.txt also states only the first one should be taken
|
1057
|
|
- // TODO: The consumers do not care about the port or the square brackets
|
1058
|
|
- // either. Remove them when integrating this function with the rest
|
1059
|
|
- const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
|
|
760
|
+ const v6 = reply.match(/^a\s+\[([0-9a-fA-F:]+)\]:\d{1,5}$/m);
|
1060
|
761
|
if (v6) {
|
1061
|
762
|
addresses.push(v6[1]);
|
1062
|
763
|
}
|
... |
... |
@@ -1091,23 +792,6 @@ class TorController { |
1091
|
792
|
|
1092
|
793
|
// Configuration
|
1093
|
794
|
|
1094
|
|
- /**
|
1095
|
|
- * Sends a GETCONF for a single key.
|
1096
|
|
- * GETCONF with a single argument returns results with one or more lines that
|
1097
|
|
- * look like `250[- ]key=value`.
|
1098
|
|
- * Any GETCONF lines that contain a single keyword only are currently dropped.
|
1099
|
|
- * So we can use similar parsing to that for getInfo.
|
1100
|
|
- *
|
1101
|
|
- * @param {string} key The key to get value for
|
1102
|
|
- * @returns {any} A parsed config value (it depends if a parser is known)
|
1103
|
|
- */
|
1104
|
|
- async getConf(key) {
|
1105
|
|
- this.#expectString(key, "key");
|
1106
|
|
- return this.#getMultipleResponseValues(
|
1107
|
|
- await this.sendCommand(`getconf ${key}`)
|
1108
|
|
- );
|
1109
|
|
- }
|
1110
|
|
-
|
1111
|
795
|
/**
|
1112
|
796
|
* Sends a GETCONF for a single key.
|
1113
|
797
|
* The function could be easily generalized to get multiple keys at once, but
|
... |
... |
@@ -1264,12 +948,14 @@ class TorController { |
1264
|
948
|
// TODO: Change the consumer and make the fields more consistent with what
|
1265
|
949
|
// we get (e.g., separate key and type, and use a boolen for permanent).
|
1266
|
950
|
const info = {
|
1267
|
|
- hsAddress: match.groups.HSAddress,
|
1268
|
|
- typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
|
|
951
|
+ address: match.groups.HSAddress,
|
|
952
|
+ keyType: match.groups.KeyType,
|
|
953
|
+ keyBlob: match.groups.PrivateKeyBlob,
|
|
954
|
+ flags: [],
|
1269
|
955
|
};
|
1270
|
956
|
const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
|
1271
|
957
|
if (maybeFlags) {
|
1272
|
|
- info.Flags = maybeFlags[1];
|
|
958
|
+ info.flags = maybeFlags[1].split(",");
|
1273
|
959
|
}
|
1274
|
960
|
return info;
|
1275
|
961
|
});
|
... |
... |
@@ -1369,28 +1055,12 @@ class TorController { |
1369
|
1055
|
* first.
|
1370
|
1056
|
*
|
1371
|
1057
|
* @param {string} type The event type to catch
|
1372
|
|
- * @param {EventFilterCallback?} filter An optional callback to filter
|
1373
|
|
- * events for which the callback will be called. If null, all events will be
|
1374
|
|
- * passed.
|
1375
|
1058
|
* @param {EventCallback} callback The callback that will handle the event
|
1376
|
|
- * @param {boolean} raw Tell whether to ignore the data parser, even if
|
1377
|
|
- * supported
|
1378
|
1059
|
*/
|
1379
|
|
- watchEvent(type, filter, callback, raw = false) {
|
|
1060
|
+ watchEvent(type, callback) {
|
1380
|
1061
|
this.#expectString(type, "type");
|
1381
|
1062
|
const start = `650 ${type}`;
|
1382
|
|
- this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
|
1383
|
|
- // Remove also the initial text
|
1384
|
|
- const dataText = message.substring(start.length + 1);
|
1385
|
|
- const parser = this.#eventParsers[type.toLowerCase()];
|
1386
|
|
- const data = dataText && parser ? parser(dataText) : null;
|
1387
|
|
- // FIXME: This is the original code, but we risk of not filtering on the
|
1388
|
|
- // data, if we ask for raw data (which we always do at the moment, but we
|
1389
|
|
- // do not use a filter either...)
|
1390
|
|
- if (filter === null || filter(data)) {
|
1391
|
|
- callback(data && !raw ? data : message);
|
1392
|
|
- }
|
1393
|
|
- });
|
|
1063
|
+ this.#socket.addNotificationCallback(new RegExp(`^${start}`), callback);
|
1394
|
1064
|
}
|
1395
|
1065
|
|
1396
|
1066
|
// Other helpers
|
... |
... |
@@ -1453,19 +1123,6 @@ class TorController { |
1453
|
1123
|
)
|
1454
|
1124
|
);
|
1455
|
1125
|
}
|
1456
|
|
-
|
1457
|
|
- /**
|
1458
|
|
- * Process multiple responses to a GETINFO or GETCONF request.
|
1459
|
|
- *
|
1460
|
|
- * @param {string} message The message to process
|
1461
|
|
- * @returns {object[]} The keys depend on the message
|
1462
|
|
- */
|
1463
|
|
- #getMultipleResponseValues(message) {
|
1464
|
|
- return info
|
1465
|
|
- .keyValueStringsFromMessage(message)
|
1466
|
|
- .map(info.stringToValue)
|
1467
|
|
- .filter(x => x);
|
1468
|
|
- }
|
1469
|
1126
|
}
|
1470
|
1127
|
|
1471
|
1128
|
const controlPortInfo = {};
|
toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
... |
... |
@@ -12,6 +12,11 @@ import { |
12
|
12
|
|
13
|
13
|
const lazy = {};
|
14
|
14
|
|
|
15
|
+ChromeUtils.defineESModuleGetters(lazy, {
|
|
16
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
17
|
+ TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
18
|
+});
|
|
19
|
+
|
15
|
20
|
XPCOMUtils.defineLazyServiceGetters(lazy, {
|
16
|
21
|
ProtocolProxyService: [
|
17
|
22
|
"@mozilla.org/network/protocol-proxy-service;1",
|
... |
... |
@@ -19,11 +24,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, { |
19
|
24
|
],
|
20
|
25
|
});
|
21
|
26
|
|
22
|
|
-ChromeUtils.defineESModuleGetters(lazy, {
|
23
|
|
- TorMonitorTopics: "resource://gre/modules/TorMonitorService.sys.mjs",
|
24
|
|
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
|
25
|
|
-});
|
26
|
|
-
|
27
|
27
|
const logger = new ConsoleAPI({
|
28
|
28
|
prefix: "TorDomainIsolator",
|
29
|
29
|
maxLogLevel: "warn",
|
... |
... |
@@ -143,7 +143,7 @@ class TorDomainIsolatorImpl { |
143
|
143
|
|
144
|
144
|
Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
|
145
|
145
|
Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
|
146
|
|
- Services.obs.addObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
|
|
146
|
+ Services.obs.addObserver(this, lazy.TorProviderTopics.StreamSucceeded);
|
147
|
147
|
|
148
|
148
|
this.#cleanupIntervalId = setInterval(
|
149
|
149
|
this.#clearKnownCircuits.bind(this),
|
... |
... |
@@ -158,7 +158,7 @@ class TorDomainIsolatorImpl { |
158
|
158
|
uninit() {
|
159
|
159
|
Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
|
160
|
160
|
Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
|
161
|
|
- Services.obs.removeObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
|
|
161
|
+ Services.obs.removeObserver(this, lazy.TorProviderTopics.StreamSucceeded);
|
162
|
162
|
clearInterval(this.#cleanupIntervalId);
|
163
|
163
|
this.#cleanupIntervalId = null;
|
164
|
164
|
this.clearIsolation();
|
... |
... |
@@ -257,12 +257,12 @@ class TorDomainIsolatorImpl { |
257
|
257
|
);
|
258
|
258
|
this.clearIsolation();
|
259
|
259
|
try {
|
260
|
|
- await lazy.TorProtocolService.newnym();
|
|
260
|
+ await lazy.TorProviderBuilder.build().newnym();
|
261
|
261
|
} catch (e) {
|
262
|
262
|
logger.error("Could not send the newnym command", e);
|
263
|
263
|
// TODO: What UX to use here? See tor-browser#41708
|
264
|
264
|
}
|
265
|
|
- } else if (topic === lazy.TorMonitorTopics.StreamSucceeded) {
|
|
265
|
+ } else if (topic === lazy.TorProviderTopics.StreamSucceeded) {
|
266
|
266
|
const { username, password, circuit } = subject.wrappedJSObject;
|
267
|
267
|
this.#updateCircuit(username, password, circuit);
|
268
|
268
|
}
|
... |
... |
@@ -553,7 +553,7 @@ class TorDomainIsolatorImpl { |
553
|
553
|
|
554
|
554
|
data = await Promise.all(
|
555
|
555
|
circuit.map(fingerprint =>
|
556
|
|
- lazy.TorProtocolService.getNodeInfo(fingerprint)
|
|
556
|
+ lazy.TorProviderBuilder.build().getNodeInfo(fingerprint)
|
557
|
557
|
)
|
558
|
558
|
);
|
559
|
559
|
this.#knownCircuits.set(id, data);
|
toolkit/components/tor-launcher/TorMonitorService.sys.mjs
deleted
1
|
|
-// Copyright (c) 2022, The Tor Project, Inc.
|
2
|
|
-
|
3
|
|
-import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
|
4
|
|
-
|
5
|
|
-const lazy = {};
|
6
|
|
-ChromeUtils.defineESModuleGetters(lazy, {
|
7
|
|
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
|
8
|
|
-});
|
9
|
|
-
|
10
|
|
-export const TorMonitorTopics = Object.freeze({
|
11
|
|
- BridgeChanged: TorProviderTopics.BridgeChanged,
|
12
|
|
- StreamSucceeded: TorProviderTopics.StreamSucceeded,
|
13
|
|
-});
|
14
|
|
-
|
15
|
|
-/**
|
16
|
|
- * This service monitors an existing Tor instance, or starts one, if needed, and
|
17
|
|
- * then starts monitoring it.
|
18
|
|
- *
|
19
|
|
- * This is the service which should be queried to know information about the
|
20
|
|
- * status of the bootstrap, the logs, etc...
|
21
|
|
- */
|
22
|
|
-export const TorMonitorService = {
|
23
|
|
- get currentBridge() {
|
24
|
|
- return lazy.TorProtocolService.currentBridge;
|
25
|
|
- },
|
26
|
|
-
|
27
|
|
- get ownsTorDaemon() {
|
28
|
|
- return lazy.TorProtocolService.ownsTorDaemon;
|
29
|
|
- },
|
30
|
|
-
|
31
|
|
- get isRunning() {
|
32
|
|
- return lazy.TorProtocolService.isRunning;
|
33
|
|
- },
|
34
|
|
-
|
35
|
|
- get isBootstrapDone() {
|
36
|
|
- return lazy.TorProtocolService.isBootstrapDone;
|
37
|
|
- },
|
38
|
|
-
|
39
|
|
- getLog() {
|
40
|
|
- return lazy.TorProtocolService.getLog();
|
41
|
|
- },
|
42
|
|
-}; |
toolkit/components/tor-launcher/TorParsers.sys.mjs
... |
... |
@@ -269,11 +269,14 @@ export const TorParsers = Object.freeze({ |
269
|
269
|
},
|
270
|
270
|
|
271
|
271
|
parseBridgeLine(line) {
|
|
272
|
+ if (!line) {
|
|
273
|
+ return null;
|
|
274
|
+ }
|
272
|
275
|
const re =
|
273
|
276
|
/\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
|
274
|
277
|
const match = re.exec(line);
|
275
|
278
|
if (!match) {
|
276
|
|
- throw new Error("Invalid bridge line.");
|
|
279
|
+ throw new Error(`Invalid bridge line: ${line}.`);
|
277
|
280
|
}
|
278
|
281
|
const bridge = match.groups;
|
279
|
282
|
if (!bridge.transport) {
|
toolkit/components/tor-launcher/TorProtocolService.sys.mjs
→
toolkit/components/tor-launcher/TorProvider.sys.mjs
1
|
|
-// Copyright (c) 2021, The Tor Project, Inc.
|
|
1
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2
|
4
|
|
3
|
5
|
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
4
|
6
|
import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
|
... |
... |
@@ -11,7 +13,6 @@ import { |
11
|
13
|
import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
|
12
|
14
|
|
13
|
15
|
const lazy = {};
|
14
|
|
-
|
15
|
16
|
ChromeUtils.defineESModuleGetters(lazy, {
|
16
|
17
|
controller: "resource://gre/modules/TorControlPort.sys.mjs",
|
17
|
18
|
configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
|
... |
... |
@@ -21,7 +22,8 @@ ChromeUtils.defineESModuleGetters(lazy, { |
21
|
22
|
|
22
|
23
|
const logger = new ConsoleAPI({
|
23
|
24
|
maxLogLevel: "warn",
|
24
|
|
- prefix: "TorProtocolService",
|
|
25
|
+ maxLogLevelPref: "browser.tor_provider.log_level",
|
|
26
|
+ prefix: "TorProvider",
|
25
|
27
|
});
|
26
|
28
|
|
27
|
29
|
/**
|
... |
... |
@@ -70,7 +72,7 @@ const ControlConnTimings = Object.freeze({ |
70
|
72
|
* It can start a new tor instance, or connect to an existing one.
|
71
|
73
|
* In the former case, it also takes its ownership by default.
|
72
|
74
|
*/
|
73
|
|
-class TorProvider {
|
|
75
|
+export class TorProvider {
|
74
|
76
|
#inited = false;
|
75
|
77
|
|
76
|
78
|
// Maintain a map of tor settings set by Tor Browser so that we don't
|
... |
... |
@@ -85,7 +87,6 @@ class TorProvider { |
85
|
87
|
#SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
|
86
|
88
|
|
87
|
89
|
#controlConnection = null; // This is cached and reused.
|
88
|
|
- #connectionQueue = [];
|
89
|
90
|
|
90
|
91
|
// Public methods
|
91
|
92
|
|
... |
... |
@@ -123,39 +124,34 @@ class TorProvider { |
123
|
124
|
// takes a Map containing tor settings
|
124
|
125
|
// throws on error
|
125
|
126
|
async writeSettings(aSettingsObj) {
|
|
127
|
+ const entries =
|
|
128
|
+ aSettingsObj instanceof Map
|
|
129
|
+ ? Array.from(aSettingsObj.entries())
|
|
130
|
+ : Object.entries(aSettingsObj);
|
126
|
131
|
// only write settings that have changed
|
127
|
|
- const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
|
128
|
|
- // make sure we have valid data here
|
129
|
|
- this.#assertValidSetting(setting, value);
|
130
|
|
-
|
|
132
|
+ const newSettings = entries.filter(([setting, value]) => {
|
131
|
133
|
if (!this.#settingsCache.has(setting)) {
|
132
|
134
|
// no cached setting, so write
|
133
|
135
|
return true;
|
134
|
136
|
}
|
135
|
137
|
|
136
|
138
|
const cachedValue = this.#settingsCache.get(setting);
|
137
|
|
- if (value === cachedValue) {
|
138
|
|
- return false;
|
139
|
|
- } else if (Array.isArray(value) && Array.isArray(cachedValue)) {
|
140
|
|
- // compare arrays member-wise
|
141
|
|
- if (value.length !== cachedValue.length) {
|
142
|
|
- return true;
|
143
|
|
- }
|
144
|
|
- for (let i = 0; i < value.length; i++) {
|
145
|
|
- if (value[i] !== cachedValue[i]) {
|
146
|
|
- return true;
|
147
|
|
- }
|
148
|
|
- }
|
149
|
|
- return false;
|
|
139
|
+ // Arrays are the only special case for which === could fail.
|
|
140
|
+ // The other values we accept (strings, booleans, numbers, null and
|
|
141
|
+ // undefined) work correctly with ===.
|
|
142
|
+ if (Array.isArray(value) && Array.isArray(cachedValue)) {
|
|
143
|
+ return (
|
|
144
|
+ value.length !== cachedValue.length ||
|
|
145
|
+ value.some((val, idx) => val !== cachedValue[idx])
|
|
146
|
+ );
|
150
|
147
|
}
|
151
|
|
- // some other different values
|
152
|
|
- return true;
|
|
148
|
+ return value !== cachedValue;
|
153
|
149
|
});
|
154
|
150
|
|
155
|
151
|
// only write if new setting to save
|
156
|
152
|
if (newSettings.length) {
|
157
|
|
- const settingsObject = Object.fromEntries(newSettings);
|
158
|
|
- await this.setConfWithReply(settingsObject);
|
|
153
|
+ const conn = await this.#getConnection();
|
|
154
|
+ await conn.setConf(Object.fromEntries(newSettings));
|
159
|
155
|
|
160
|
156
|
// save settings to cache after successfully writing to Tor
|
161
|
157
|
for (const [setting, value] of newSettings) {
|
... |
... |
@@ -164,23 +160,15 @@ class TorProvider { |
164
|
160
|
}
|
165
|
161
|
}
|
166
|
162
|
|
167
|
|
- async readStringArraySetting(aSetting) {
|
168
|
|
- const value = await this.#readSetting(aSetting);
|
169
|
|
- this.#settingsCache.set(aSetting, value);
|
170
|
|
- return value;
|
171
|
|
- }
|
172
|
|
-
|
173
|
163
|
// writes current tor settings to disk
|
174
|
164
|
async flushSettings() {
|
175
|
|
- await this.sendCommand("SAVECONF");
|
|
165
|
+ const conn = await this.#getConnection();
|
|
166
|
+ await conn.flushSettings();
|
176
|
167
|
}
|
177
|
168
|
|
178
|
169
|
async connect() {
|
179
|
|
- const kTorConfKeyDisableNetwork = "DisableNetwork";
|
180
|
|
- const settings = {};
|
181
|
|
- settings[kTorConfKeyDisableNetwork] = false;
|
182
|
|
- await this.setConfWithReply(settings);
|
183
|
|
- await this.sendCommand("SAVECONF");
|
|
170
|
+ const conn = await this.#getConnection();
|
|
171
|
+ await conn.setNetworkEnabled(true);
|
184
|
172
|
this.clearBootstrapError();
|
185
|
173
|
this.retrieveBootstrapStatus();
|
186
|
174
|
}
|
... |
... |
@@ -188,12 +176,8 @@ class TorProvider { |
188
|
176
|
async stopBootstrap() {
|
189
|
177
|
// Tell tor to disable use of the network; this should stop the bootstrap
|
190
|
178
|
// process.
|
191
|
|
- try {
|
192
|
|
- const settings = { DisableNetwork: true };
|
193
|
|
- await this.setConfWithReply(settings);
|
194
|
|
- } catch (e) {
|
195
|
|
- logger.error("Error stopping bootstrap", e);
|
196
|
|
- }
|
|
179
|
+ const conn = await this.#getConnection();
|
|
180
|
+ await conn.setNetworkEnabled(false);
|
197
|
181
|
// We are not interested in waiting for this, nor in **catching its error**,
|
198
|
182
|
// so we do not await this. We just want to be notified when the bootstrap
|
199
|
183
|
// status is actually updated through observers.
|
... |
... |
@@ -201,28 +185,31 @@ class TorProvider { |
201
|
185
|
}
|
202
|
186
|
|
203
|
187
|
async newnym() {
|
204
|
|
- return this.sendCommand("SIGNAL NEWNYM");
|
|
188
|
+ const conn = await this.#getConnection();
|
|
189
|
+ await conn.newnym();
|
205
|
190
|
}
|
206
|
191
|
|
207
|
192
|
// Ask tor which ports it is listening to for SOCKS connections.
|
208
|
193
|
// At the moment this is used only in TorCheckService.
|
209
|
194
|
async getSocksListeners() {
|
210
|
|
- const cmd = "GETINFO";
|
211
|
|
- const keyword = "net/listeners/socks";
|
212
|
|
- const response = await this.sendCommand(cmd, keyword);
|
213
|
|
- return TorParsers.parseReply(cmd, keyword, response);
|
|
195
|
+ const conn = await this.#getConnection();
|
|
196
|
+ return conn.getSocksListeners();
|
214
|
197
|
}
|
215
|
198
|
|
216
|
199
|
async getBridges() {
|
|
200
|
+ const conn = await this.#getConnection();
|
217
|
201
|
// Ideally, we would not need this function, because we should be the one
|
218
|
202
|
// setting them with TorSettings. However, TorSettings is not notified of
|
219
|
203
|
// change of settings. So, asking tor directly with the control connection
|
220
|
204
|
// is the most reliable way of getting the configured bridges, at the
|
221
|
205
|
// moment. Also, we are using this for the circuit display, which should
|
222
|
206
|
// work also when we are not configuring the tor daemon, but just using it.
|
223
|
|
- return this.#withConnection(conn => {
|
224
|
|
- return conn.getConf("bridge");
|
225
|
|
- });
|
|
207
|
+ return conn.getBridges();
|
|
208
|
+ }
|
|
209
|
+
|
|
210
|
+ async getPluggableTransports() {
|
|
211
|
+ const conn = await this.#getConnection();
|
|
212
|
+ return conn.getPluggableTransports();
|
226
|
213
|
}
|
227
|
214
|
|
228
|
215
|
/**
|
... |
... |
@@ -232,68 +219,55 @@ class TorProvider { |
232
|
219
|
* @returns {Promise<NodeData>}
|
233
|
220
|
*/
|
234
|
221
|
async getNodeInfo(id) {
|
235
|
|
- return this.#withConnection(async conn => {
|
236
|
|
- const node = {
|
237
|
|
- fingerprint: id,
|
238
|
|
- ipAddrs: [],
|
239
|
|
- bridgeType: null,
|
240
|
|
- regionCode: null,
|
241
|
|
- };
|
242
|
|
- const bridge = (await conn.getConf("bridge"))?.find(
|
243
|
|
- foundBridge => foundBridge.ID?.toUpperCase() === id.toUpperCase()
|
244
|
|
- );
|
245
|
|
- const addrRe = /^\[?([^\]]+)\]?:\d+$/;
|
246
|
|
- if (bridge) {
|
247
|
|
- node.bridgeType = bridge.type ?? "";
|
248
|
|
- // Attempt to get an IP address from bridge address string.
|
249
|
|
- const ip = bridge.address.match(addrRe)?.[1];
|
250
|
|
- if (ip && !ip.startsWith("0.")) {
|
251
|
|
- node.ipAddrs.push(ip);
|
252
|
|
- }
|
253
|
|
- } else {
|
254
|
|
- // Either dealing with a relay, or a bridge whose fingerprint is not
|
255
|
|
- // saved in torrc.
|
256
|
|
- const info = await conn.getInfo(`ns/id/${id}`);
|
257
|
|
- if (info.IP && !info.IP.startsWith("0.")) {
|
258
|
|
- node.ipAddrs.push(info.IP);
|
259
|
|
- }
|
260
|
|
- const ip6 = info.IPv6?.match(addrRe)?.[1];
|
261
|
|
- if (ip6) {
|
262
|
|
- node.ipAddrs.push(ip6);
|
263
|
|
- }
|
|
222
|
+ const conn = await this.#getConnection();
|
|
223
|
+ const node = {
|
|
224
|
+ fingerprint: id,
|
|
225
|
+ ipAddrs: [],
|
|
226
|
+ bridgeType: null,
|
|
227
|
+ regionCode: null,
|
|
228
|
+ };
|
|
229
|
+ const bridge = (await conn.getBridges())?.find(
|
|
230
|
+ foundBridge => foundBridge.id?.toUpperCase() === id.toUpperCase()
|
|
231
|
+ );
|
|
232
|
+ if (bridge) {
|
|
233
|
+ node.bridgeType = bridge.transport ?? "";
|
|
234
|
+ // Attempt to get an IP address from bridge address string.
|
|
235
|
+ const ip = bridge.addr.match(/^\[?([^\]]+)\]?:\d+$/)?.[1];
|
|
236
|
+ if (ip && !ip.startsWith("0.")) {
|
|
237
|
+ node.ipAddrs.push(ip);
|
264
|
238
|
}
|
265
|
|
- if (node.ipAddrs.length) {
|
266
|
|
- // Get the country code for the node's IP address.
|
267
|
|
- let regionCode;
|
268
|
|
- try {
|
269
|
|
- // Expect a 2-letter ISO3166-1 code, which should also be a valid
|
270
|
|
- // BCP47 Region subtag.
|
271
|
|
- regionCode = await conn.getInfo("ip-to-country/" + node.ipAddrs[0]);
|
272
|
|
- } catch {}
|
|
239
|
+ } else {
|
|
240
|
+ node.ipAddrs = await conn.getNodeAddresses(id);
|
|
241
|
+ }
|
|
242
|
+ if (node.ipAddrs.length) {
|
|
243
|
+ // Get the country code for the node's IP address.
|
|
244
|
+ try {
|
|
245
|
+ // Expect a 2-letter ISO3166-1 code, which should also be a valid
|
|
246
|
+ // BCP47 Region subtag.
|
|
247
|
+ const regionCode = await conn.getIPCountry(node.ipAddrs[0]);
|
273
|
248
|
if (regionCode && regionCode !== "??") {
|
274
|
249
|
node.regionCode = regionCode.toUpperCase();
|
275
|
250
|
}
|
|
251
|
+ } catch (e) {
|
|
252
|
+ logger.warn(`Cannot get a country for IP ${node.ipAddrs[0]}`, e);
|
276
|
253
|
}
|
277
|
|
- return node;
|
278
|
|
- });
|
|
254
|
+ }
|
|
255
|
+ return node;
|
279
|
256
|
}
|
280
|
257
|
|
281
|
|
- async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
|
282
|
|
- return this.#withConnection(conn => {
|
283
|
|
- return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
|
284
|
|
- });
|
|
258
|
+ async onionAuthAdd(address, b64PrivateKey, isPermanent) {
|
|
259
|
+ const conn = await this.#getConnection();
|
|
260
|
+ return conn.onionAuthAdd(address, b64PrivateKey, isPermanent);
|
285
|
261
|
}
|
286
|
262
|
|
287
|
|
- async onionAuthRemove(hsAddress) {
|
288
|
|
- return this.#withConnection(conn => {
|
289
|
|
- return conn.onionAuthRemove(hsAddress);
|
290
|
|
- });
|
|
263
|
+ async onionAuthRemove(address) {
|
|
264
|
+ const conn = await this.#getConnection();
|
|
265
|
+ return conn.onionAuthRemove(address);
|
291
|
266
|
}
|
292
|
267
|
|
293
|
268
|
async onionAuthViewKeys() {
|
294
|
|
- return this.#withConnection(conn => {
|
295
|
|
- return conn.onionAuthViewKeys();
|
296
|
|
- });
|
|
269
|
+ const conn = await this.#getConnection();
|
|
270
|
+ return conn.onionAuthViewKeys();
|
297
|
271
|
}
|
298
|
272
|
|
299
|
273
|
// TODO: transform the following 4 functions in getters.
|
... |
... |
@@ -333,106 +307,6 @@ class TorProvider { |
333
|
307
|
return this.#SOCKSPortInfo;
|
334
|
308
|
}
|
335
|
309
|
|
336
|
|
- // Public, but called only internally
|
337
|
|
-
|
338
|
|
- // Executes a command on the control port.
|
339
|
|
- // Return a reply object or null if a fatal error occurs.
|
340
|
|
- async sendCommand(cmd, args) {
|
341
|
|
- const maxTimeout = 1000;
|
342
|
|
- let leftConnAttempts = 5;
|
343
|
|
- let timeout = 250;
|
344
|
|
- let reply;
|
345
|
|
- while (leftConnAttempts-- > 0) {
|
346
|
|
- const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
|
347
|
|
- if (response.connected) {
|
348
|
|
- reply = response.reply;
|
349
|
|
- break;
|
350
|
|
- }
|
351
|
|
- // We failed to acquire the controller after multiple attempts.
|
352
|
|
- // Try again after some time.
|
353
|
|
- logger.warn(
|
354
|
|
- "sendCommand: Acquiring control connection failed, trying again later.",
|
355
|
|
- cmd,
|
356
|
|
- args
|
357
|
|
- );
|
358
|
|
- await new Promise(resolve => setTimeout(() => resolve(), timeout));
|
359
|
|
- timeout = Math.min(2 * timeout, maxTimeout);
|
360
|
|
- }
|
361
|
|
-
|
362
|
|
- // We sent the command, but we still got an empty response.
|
363
|
|
- // Something must be busted elsewhere.
|
364
|
|
- if (!reply) {
|
365
|
|
- throw new Error(`${cmd} sent an empty response`);
|
366
|
|
- }
|
367
|
|
-
|
368
|
|
- // TODO: Move the parsing of the reply to the controller, because anyone
|
369
|
|
- // calling sendCommand on it actually wants a parsed reply.
|
370
|
|
-
|
371
|
|
- reply = TorParsers.parseCommandResponse(reply);
|
372
|
|
- if (!TorParsers.commandSucceeded(reply)) {
|
373
|
|
- if (reply?.lineArray) {
|
374
|
|
- throw new Error(reply.lineArray.join("\n"));
|
375
|
|
- }
|
376
|
|
- throw new Error(`${cmd} failed with code ${reply.statusCode}`);
|
377
|
|
- }
|
378
|
|
-
|
379
|
|
- return reply;
|
380
|
|
- }
|
381
|
|
-
|
382
|
|
- // Perform a SETCONF command.
|
383
|
|
- // aSettingsObj should be a _javascript_ object with keys (property values)
|
384
|
|
- // that correspond to tor config. keys. The value associated with each
|
385
|
|
- // key should be a simple string, a string array, or a Boolean value.
|
386
|
|
- // If an associated value is undefined or null, a key with no value is
|
387
|
|
- // passed in the SETCONF command.
|
388
|
|
- // Throws in case of error, or returns a reply object.
|
389
|
|
- async setConfWithReply(settings) {
|
390
|
|
- if (!settings) {
|
391
|
|
- throw new Error("Empty settings object");
|
392
|
|
- }
|
393
|
|
- const args = Object.entries(settings)
|
394
|
|
- .map(([key, val]) => {
|
395
|
|
- if (val === undefined || val === null) {
|
396
|
|
- return key;
|
397
|
|
- }
|
398
|
|
- const valType = typeof val;
|
399
|
|
- let rv = `${key}=`;
|
400
|
|
- if (valType === "boolean") {
|
401
|
|
- rv += val ? "1" : "0";
|
402
|
|
- } else if (Array.isArray(val)) {
|
403
|
|
- rv += val.map(TorParsers.escapeString).join(` ${key}=`);
|
404
|
|
- } else if (valType === "string") {
|
405
|
|
- rv += TorParsers.escapeString(val);
|
406
|
|
- } else {
|
407
|
|
- logger.error(`Got unsupported type for ${key}`, val);
|
408
|
|
- throw new Error(`Unsupported type ${valType} (key ${key})`);
|
409
|
|
- }
|
410
|
|
- return rv;
|
411
|
|
- })
|
412
|
|
- .filter(arg => arg);
|
413
|
|
- if (!args.length) {
|
414
|
|
- throw new Error("No settings to set");
|
415
|
|
- }
|
416
|
|
-
|
417
|
|
- await this.sendCommand("SETCONF", args.join(" "));
|
418
|
|
- }
|
419
|
|
-
|
420
|
|
- // Public, never called?
|
421
|
|
-
|
422
|
|
- async readBoolSetting(aSetting) {
|
423
|
|
- let value = await this.#readBoolSetting(aSetting);
|
424
|
|
- this.#settingsCache.set(aSetting, value);
|
425
|
|
- return value;
|
426
|
|
- }
|
427
|
|
-
|
428
|
|
- async readStringSetting(aSetting) {
|
429
|
|
- let value = await this.#readStringSetting(aSetting);
|
430
|
|
- this.#settingsCache.set(aSetting, value);
|
431
|
|
- return value;
|
432
|
|
- }
|
433
|
|
-
|
434
|
|
- // Private
|
435
|
|
-
|
436
|
310
|
async #setSockets() {
|
437
|
311
|
try {
|
438
|
312
|
const isWindows = TorLauncherUtil.isWindows;
|
... |
... |
@@ -511,167 +385,24 @@ class TorProvider { |
511
|
385
|
}
|
512
|
386
|
}
|
513
|
387
|
|
514
|
|
- #assertValidSettingKey(aSetting) {
|
515
|
|
- // ensure the 'key' is a string
|
516
|
|
- if (typeof aSetting !== "string") {
|
517
|
|
- throw new Error(
|
518
|
|
- `Expected setting of type string but received ${typeof aSetting}`
|
519
|
|
- );
|
520
|
|
- }
|
521
|
|
- }
|
522
|
|
-
|
523
|
|
- #assertValidSetting(aSetting, aValue) {
|
524
|
|
- this.#assertValidSettingKey(aSetting);
|
525
|
|
- switch (typeof aValue) {
|
526
|
|
- case "boolean":
|
527
|
|
- case "string":
|
528
|
|
- return;
|
529
|
|
- case "object":
|
530
|
|
- if (aValue === null) {
|
531
|
|
- return;
|
532
|
|
- } else if (Array.isArray(aValue)) {
|
533
|
|
- for (const element of aValue) {
|
534
|
|
- if (typeof element !== "string") {
|
535
|
|
- throw new Error(
|
536
|
|
- `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
|
537
|
|
- );
|
538
|
|
- }
|
539
|
|
- }
|
540
|
|
- return;
|
541
|
|
- }
|
542
|
|
- // fall through
|
543
|
|
- default:
|
544
|
|
- throw new Error(
|
545
|
|
- `Invalid object type received for setting '${aSetting}'`
|
546
|
|
- );
|
547
|
|
- }
|
548
|
|
- }
|
549
|
|
-
|
550
|
|
- // Perform a GETCONF command.
|
551
|
|
- async #readSetting(aSetting) {
|
552
|
|
- this.#assertValidSettingKey(aSetting);
|
553
|
|
-
|
554
|
|
- const cmd = "GETCONF";
|
555
|
|
- let reply = await this.sendCommand(cmd, aSetting);
|
556
|
|
- return TorParsers.parseReply(cmd, aSetting, reply);
|
557
|
|
- }
|
558
|
|
-
|
559
|
|
- async #readStringSetting(aSetting) {
|
560
|
|
- let lineArray = await this.#readSetting(aSetting);
|
561
|
|
- if (lineArray.length !== 1) {
|
562
|
|
- throw new Error(
|
563
|
|
- `Expected an array with length 1 but received array of length ${lineArray.length}`
|
564
|
|
- );
|
565
|
|
- }
|
566
|
|
- return lineArray[0];
|
567
|
|
- }
|
568
|
|
-
|
569
|
|
- async #readBoolSetting(aSetting) {
|
570
|
|
- const value = this.#readStringSetting(aSetting);
|
571
|
|
- switch (value) {
|
572
|
|
- case "0":
|
573
|
|
- return false;
|
574
|
|
- case "1":
|
575
|
|
- return true;
|
576
|
|
- default:
|
577
|
|
- throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
|
578
|
|
- }
|
579
|
|
- }
|
580
|
|
-
|
581
|
|
- async #trySend(cmd, args, rethrow) {
|
582
|
|
- let connected = false;
|
583
|
|
- let reply;
|
584
|
|
- let leftAttempts = 2;
|
585
|
|
- while (leftAttempts-- > 0) {
|
586
|
|
- let conn;
|
587
|
|
- try {
|
588
|
|
- conn = await this.#getConnection();
|
589
|
|
- } catch (e) {
|
590
|
|
- logger.error("Cannot get a connection to the control port", e);
|
591
|
|
- if (leftAttempts == 0 && rethrow) {
|
592
|
|
- throw e;
|
593
|
|
- }
|
594
|
|
- }
|
595
|
|
- if (!conn) {
|
596
|
|
- continue;
|
597
|
|
- }
|
598
|
|
- // If we _ever_ got a connection, the caller should not try again
|
599
|
|
- connected = true;
|
600
|
|
- try {
|
601
|
|
- reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
|
602
|
|
- if (reply) {
|
603
|
|
- // Return for reuse.
|
604
|
|
- this.#returnConnection();
|
605
|
|
- } else {
|
606
|
|
- // Connection is bad.
|
607
|
|
- logger.warn(
|
608
|
|
- "sendCommand returned an empty response, taking the connection as broken and closing it."
|
609
|
|
- );
|
610
|
|
- this.#closeConnection();
|
611
|
|
- }
|
612
|
|
- } catch (e) {
|
613
|
|
- logger.error(`Cannot send the command ${cmd}`, e);
|
614
|
|
- this.#closeConnection();
|
615
|
|
- if (leftAttempts == 0 && rethrow) {
|
616
|
|
- throw e;
|
617
|
|
- }
|
618
|
|
- }
|
619
|
|
- }
|
620
|
|
- return { connected, reply };
|
621
|
|
- }
|
622
|
|
-
|
623
|
|
- // Opens an authenticated connection, sets it to this.#controlConnection, and
|
624
|
|
- // return it.
|
625
|
388
|
async #getConnection() {
|
626
|
|
- if (!this.#controlConnection) {
|
|
389
|
+ if (!this.#controlConnection?.isOpen) {
|
627
|
390
|
this.#controlConnection = await lazy.controller();
|
628
|
391
|
}
|
629
|
|
- if (this.#controlConnection.inUse) {
|
630
|
|
- await new Promise((resolve, reject) =>
|
631
|
|
- this.#connectionQueue.push({ resolve, reject })
|
632
|
|
- );
|
633
|
|
- } else {
|
634
|
|
- this.#controlConnection.inUse = true;
|
635
|
|
- }
|
636
|
392
|
return this.#controlConnection;
|
637
|
393
|
}
|
638
|
394
|
|
639
|
|
- #returnConnection() {
|
640
|
|
- if (this.#connectionQueue.length) {
|
641
|
|
- this.#connectionQueue.shift().resolve();
|
642
|
|
- } else {
|
643
|
|
- this.#controlConnection.inUse = false;
|
644
|
|
- }
|
645
|
|
- }
|
646
|
|
-
|
647
|
|
- async #withConnection(func) {
|
648
|
|
- // TODO: Make more robust?
|
649
|
|
- const conn = await this.#getConnection();
|
650
|
|
- try {
|
651
|
|
- return await func(conn);
|
652
|
|
- } finally {
|
653
|
|
- this.#returnConnection();
|
654
|
|
- }
|
655
|
|
- }
|
656
|
|
-
|
657
|
|
- // If aConn is omitted, the cached connection is closed.
|
658
|
395
|
#closeConnection() {
|
659
|
396
|
if (this.#controlConnection) {
|
660
|
397
|
logger.info("Closing the control connection");
|
661
|
398
|
this.#controlConnection.close();
|
662
|
399
|
this.#controlConnection = null;
|
663
|
400
|
}
|
664
|
|
- for (const promise of this.#connectionQueue) {
|
665
|
|
- promise.reject("Connection closed");
|
666
|
|
- }
|
667
|
|
- this.#connectionQueue = [];
|
668
|
401
|
}
|
669
|
402
|
|
670
|
403
|
async #reconnect() {
|
671
|
404
|
this.#closeConnection();
|
672
|
|
- const conn = await this.#getConnection();
|
673
|
|
- logger.debug("Reconnected to the control port.");
|
674
|
|
- this.#returnConnection(conn);
|
|
405
|
+ await this.#getConnection();
|
675
|
406
|
}
|
676
|
407
|
|
677
|
408
|
async #readAuthenticationCookie(aPath) {
|
... |
... |
@@ -777,8 +508,9 @@ class TorProvider { |
777
|
508
|
if (this.ownsTorDaemon) {
|
778
|
509
|
// When we own the tor daemon, we listen to more events, that are used
|
779
|
510
|
// for about:torconnect or for showing the logs in the settings page.
|
780
|
|
- this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
|
781
|
|
- this._processBootstrapStatus(lines[0], false)
|
|
511
|
+ this._eventHandlers.set(
|
|
512
|
+ "STATUS_CLIENT",
|
|
513
|
+ this._processStatusClient.bind(this)
|
782
|
514
|
);
|
783
|
515
|
this._eventHandlers.set("NOTICE", this._processLog.bind(this));
|
784
|
516
|
this._eventHandlers.set("WARN", this._processLog.bind(this));
|
... |
... |
@@ -809,23 +541,10 @@ class TorProvider { |
809
|
541
|
throw new Error("Event monitor connection not available");
|
810
|
542
|
}
|
811
|
543
|
|
812
|
|
- // TODO: Unify with TorProtocolService.sendCommand and put everything in the
|
813
|
|
- // reviewed torbutton replacement.
|
814
|
|
- const cmd = "GETINFO";
|
815
|
|
- const key = "status/bootstrap-phase";
|
816
|
|
- let reply = await this._connection.sendCommand(`${cmd} ${key}`);
|
817
|
|
-
|
818
|
|
- // A typical reply looks like:
|
819
|
|
- // 250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
|
820
|
|
- // 250 OK
|
821
|
|
- reply = TorParsers.parseCommandResponse(reply);
|
822
|
|
- if (!TorParsers.commandSucceeded(reply)) {
|
823
|
|
- throw new Error(`${cmd} failed`);
|
824
|
|
- }
|
825
|
|
- reply = TorParsers.parseReply(cmd, key, reply);
|
826
|
|
- if (reply.length) {
|
827
|
|
- this._processBootstrapStatus(reply[0], true);
|
828
|
|
- }
|
|
544
|
+ this._processBootstrapStatus(
|
|
545
|
+ await this._connection.getBootstrapPhase(),
|
|
546
|
+ true
|
|
547
|
+ );
|
829
|
548
|
}
|
830
|
549
|
|
831
|
550
|
// Returns captured log message as a text string (one message per line).
|
... |
... |
@@ -1058,37 +777,32 @@ class TorProvider { |
1058
|
777
|
_monitorEvent(type, callback) {
|
1059
|
778
|
logger.info(`Watching events of type ${type}.`);
|
1060
|
779
|
let replyObj = {};
|
1061
|
|
- this._connection.watchEvent(
|
1062
|
|
- type,
|
1063
|
|
- null,
|
1064
|
|
- line => {
|
1065
|
|
- if (!line) {
|
1066
|
|
- return;
|
1067
|
|
- }
|
1068
|
|
- logger.debug("Event response: ", line);
|
1069
|
|
- const isComplete = TorParsers.parseReplyLine(line, replyObj);
|
1070
|
|
- if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
|
1071
|
|
- return;
|
1072
|
|
- }
|
1073
|
|
- const reply = replyObj;
|
1074
|
|
- replyObj = {};
|
1075
|
|
- if (reply.statusCode !== TorStatuses.EventNotification) {
|
1076
|
|
- logger.error("Unexpected event status code:", reply.statusCode);
|
1077
|
|
- return;
|
1078
|
|
- }
|
1079
|
|
- if (!reply.lineArray[0].startsWith(`${type} `)) {
|
1080
|
|
- logger.error("Wrong format for the first line:", reply.lineArray[0]);
|
1081
|
|
- return;
|
1082
|
|
- }
|
1083
|
|
- reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
|
1084
|
|
- try {
|
1085
|
|
- callback(type, reply.lineArray);
|
1086
|
|
- } catch (e) {
|
1087
|
|
- logger.error("Exception while handling an event", reply, e);
|
1088
|
|
- }
|
1089
|
|
- },
|
1090
|
|
- true
|
1091
|
|
- );
|
|
780
|
+ this._connection.watchEvent(type, line => {
|
|
781
|
+ if (!line) {
|
|
782
|
+ return;
|
|
783
|
+ }
|
|
784
|
+ logger.debug("Event response: ", line);
|
|
785
|
+ const isComplete = TorParsers.parseReplyLine(line, replyObj);
|
|
786
|
+ if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
|
|
787
|
+ return;
|
|
788
|
+ }
|
|
789
|
+ const reply = replyObj;
|
|
790
|
+ replyObj = {};
|
|
791
|
+ if (reply.statusCode !== TorStatuses.EventNotification) {
|
|
792
|
+ logger.error("Unexpected event status code:", reply.statusCode);
|
|
793
|
+ return;
|
|
794
|
+ }
|
|
795
|
+ if (!reply.lineArray[0].startsWith(`${type} `)) {
|
|
796
|
+ logger.error("Wrong format for the first line:", reply.lineArray[0]);
|
|
797
|
+ return;
|
|
798
|
+ }
|
|
799
|
+ reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
|
|
800
|
+ try {
|
|
801
|
+ callback(type, reply.lineArray);
|
|
802
|
+ } catch (e) {
|
|
803
|
+ logger.error("Exception while handling an event", reply, e);
|
|
804
|
+ }
|
|
805
|
+ });
|
1092
|
806
|
}
|
1093
|
807
|
|
1094
|
808
|
_processLog(type, lines) {
|
... |
... |
@@ -1116,15 +830,12 @@ class TorProvider { |
1116
|
830
|
// to TorBootstrapStatus observers.
|
1117
|
831
|
// If aSuppressErrors is true, errors are ignored. This is used when we
|
1118
|
832
|
// are handling the response to a "GETINFO status/bootstrap-phase" command.
|
1119
|
|
- _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
|
1120
|
|
- const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
|
1121
|
|
- if (!statusObj) {
|
1122
|
|
- return;
|
1123
|
|
- }
|
1124
|
|
-
|
|
833
|
+ _processBootstrapStatus(statusObj, suppressErrors) {
|
1125
|
834
|
// Notify observers
|
1126
|
|
- statusObj.wrappedJSObject = statusObj;
|
1127
|
|
- Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
|
|
835
|
+ Services.obs.notifyObservers(
|
|
836
|
+ { wrappedJSObject: statusObj },
|
|
837
|
+ "TorBootstrapStatus"
|
|
838
|
+ );
|
1128
|
839
|
|
1129
|
840
|
if (statusObj.PROGRESS === 100) {
|
1130
|
841
|
this._isBootstrapDone = true;
|
... |
... |
@@ -1141,7 +852,7 @@ class TorProvider { |
1141
|
852
|
if (
|
1142
|
853
|
statusObj.TYPE === "WARN" &&
|
1143
|
854
|
statusObj.RECOMMENDATION !== "ignore" &&
|
1144
|
|
- !aSuppressErrors
|
|
855
|
+ !suppressErrors
|
1145
|
856
|
) {
|
1146
|
857
|
this._notifyBootstrapError(statusObj);
|
1147
|
858
|
}
|
... |
... |
@@ -1184,6 +895,15 @@ class TorProvider { |
1184
|
895
|
}
|
1185
|
896
|
}
|
1186
|
897
|
|
|
898
|
+ _processStatusClient(_type, lines) {
|
|
899
|
+ const statusObj = TorParsers.parseBootstrapStatus(lines[0]);
|
|
900
|
+ if (!statusObj) {
|
|
901
|
+ // No `BOOTSTRAP` in the line
|
|
902
|
+ return;
|
|
903
|
+ }
|
|
904
|
+ this._processBootstrapStatus(statusObj, false);
|
|
905
|
+ }
|
|
906
|
+
|
1187
|
907
|
async _processCircEvent(_type, lines) {
|
1188
|
908
|
const builtEvent =
|
1189
|
909
|
/^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
|
... |
... |
@@ -1295,7 +1015,3 @@ class TorProvider { |
1295
|
1015
|
this.clearBootstrapError();
|
1296
|
1016
|
}
|
1297
|
1017
|
} |
1298
|
|
-
|
1299
|
|
-// TODO: Stop defining TorProtocolService, make the builder instance the
|
1300
|
|
-// TorProvider.
|
1301
|
|
-export const TorProtocolService = new TorProvider(); |
toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
... |
... |
@@ -4,7 +4,7 @@ |
4
|
4
|
|
5
|
5
|
const lazy = {};
|
6
|
6
|
ChromeUtils.defineESModuleGetters(lazy, {
|
7
|
|
- TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
|
|
7
|
+ TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
|
8
|
8
|
});
|
9
|
9
|
|
10
|
10
|
export const TorProviderTopics = Object.freeze({
|
... |
... |
@@ -19,16 +19,25 @@ export const TorProviderTopics = Object.freeze({ |
19
|
19
|
});
|
20
|
20
|
|
21
|
21
|
export class TorProviderBuilder {
|
|
22
|
+ static #provider = null;
|
|
23
|
+
|
22
|
24
|
static async init() {
|
23
|
|
- await lazy.TorProtocolService.init();
|
|
25
|
+ const provider = new lazy.TorProvider();
|
|
26
|
+ await provider.init();
|
|
27
|
+ // Assign it only when initialization succeeds.
|
|
28
|
+ TorProviderBuilder.#provider = provider;
|
24
|
29
|
}
|
25
|
30
|
|
26
|
31
|
static uninit() {
|
27
|
|
- lazy.TorProtocolService.uninit();
|
|
32
|
+ TorProviderBuilder.#provider.uninit();
|
|
33
|
+ TorProviderBuilder.#provider = null;
|
28
|
34
|
}
|
29
|
35
|
|
30
|
36
|
// TODO: Switch to an async build?
|
31
|
37
|
static build() {
|
32
|
|
- return lazy.TorProtocolService;
|
|
38
|
+ if (!TorProviderBuilder.#provider) {
|
|
39
|
+ throw new Error("TorProviderBuilder has not been initialized yet.");
|
|
40
|
+ }
|
|
41
|
+ return TorProviderBuilder.#provider;
|
33
|
42
|
}
|
34
|
43
|
} |
toolkit/components/tor-launcher/TorStartupService.sys.mjs
... |
... |
@@ -3,22 +3,13 @@ const lazy = {}; |
3
|
3
|
// We will use the modules only when the profile is loaded, so prefer lazy
|
4
|
4
|
// loading
|
5
|
5
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
6
|
+ TorConnect: "resource:///modules/TorConnect.sys.mjs",
|
6
|
7
|
TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
|
7
|
8
|
TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
|
8
|
9
|
TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
10
|
+ TorSettings: "resource:///modules/TorSettings.sys.mjs",
|
9
|
11
|
});
|
10
|
12
|
|
11
|
|
-ChromeUtils.defineModuleGetter(
|
12
|
|
- lazy,
|
13
|
|
- "TorConnect",
|
14
|
|
- "resource:///modules/TorConnect.jsm"
|
15
|
|
-);
|
16
|
|
-ChromeUtils.defineModuleGetter(
|
17
|
|
- lazy,
|
18
|
|
- "TorSettings",
|
19
|
|
- "resource:///modules/TorSettings.jsm"
|
20
|
|
-);
|
21
|
|
-
|
22
|
13
|
/* Browser observer topis */
|
23
|
14
|
const BrowserTopics = Object.freeze({
|
24
|
15
|
ProfileAfterChange: "profile-after-change",
|
toolkit/components/tor-launcher/moz.build
... |
... |
@@ -3,10 +3,9 @@ EXTRA_JS_MODULES += [ |
3
|
3
|
"TorControlPort.sys.mjs",
|
4
|
4
|
"TorDomainIsolator.sys.mjs",
|
5
|
5
|
"TorLauncherUtil.sys.mjs",
|
6
|
|
- "TorMonitorService.sys.mjs",
|
7
|
6
|
"TorParsers.sys.mjs",
|
8
|
7
|
"TorProcess.sys.mjs",
|
9
|
|
- "TorProtocolService.sys.mjs",
|
|
8
|
+ "TorProvider.sys.mjs",
|
10
|
9
|
"TorProviderBuilder.sys.mjs",
|
11
|
10
|
"TorStartupService.sys.mjs",
|
12
|
11
|
]
|
toolkit/mozapps/update/UpdateService.sys.mjs
... |
... |
@@ -23,18 +23,13 @@ ChromeUtils.defineESModuleGetters(lazy, { |
23
|
23
|
AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
|
24
|
24
|
CertUtils: "resource://gre/modules/CertUtils.sys.mjs",
|
25
|
25
|
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
|
|
26
|
+ TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
26
|
27
|
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
|
27
|
28
|
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
|
28
|
29
|
ctypes: "resource://gre/modules/ctypes.sys.mjs",
|
29
|
30
|
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
30
|
31
|
});
|
31
|
32
|
|
32
|
|
-ChromeUtils.defineModuleGetter(
|
33
|
|
- lazy,
|
34
|
|
- "TorMonitorService",
|
35
|
|
- "resource://gre/modules/TorMonitorService.jsm"
|
36
|
|
-);
|
37
|
|
-
|
38
|
33
|
XPCOMUtils.defineLazyServiceGetter(
|
39
|
34
|
lazy,
|
40
|
35
|
"AUS",
|
... |
... |
@@ -394,10 +389,11 @@ XPCOMUtils.defineLazyGetter( |
394
|
389
|
);
|
395
|
390
|
|
396
|
391
|
function _shouldRegisterBootstrapObserver(errorCode) {
|
|
392
|
+ const provider = lazy.TorProviderBuilder.build();
|
397
|
393
|
return (
|
398
|
394
|
errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
|
399
|
|
- !lazy.TorMonitorService.isBootstrapDone &&
|
400
|
|
- lazy.TorMonitorService.ownsTorDaemon
|
|
395
|
+ !provider.isBootstrapDone &&
|
|
396
|
+ provider.ownsTorDaemon
|
401
|
397
|
);
|
402
|
398
|
}
|
403
|
399
|
|
... |
... |
@@ -5833,10 +5829,7 @@ Downloader.prototype = { |
5833
|
5829
|
// we choose to compute these hashes.
|
5834
|
5830
|
hash = hash.finish(false);
|
5835
|
5831
|
digest = Array.from(hash, (c, i) =>
|
5836
|
|
- hash
|
5837
|
|
- .charCodeAt(i)
|
5838
|
|
- .toString(16)
|
5839
|
|
- .padStart(2, "0")
|
|
5832
|
+ hash.charCodeAt(i).toString(16).padStart(2, "0")
|
5840
|
5833
|
).join("");
|
5841
|
5834
|
} catch (e) {
|
5842
|
5835
|
LOG(
|
|