Commits:
-
3fcf8e84
by Elena at 2026-08-19T10:58:03+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Rename ITor... interfaces to torITor...
The prefix is basically a namespace, and really few interfaces do not
have one, so add that prefix also to our interfaces.
-
15498d90
by Elena at 2026-08-19T11:09:16+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Make it possible to remove the async event handler.
That will help when cleaning stuff up, to make sure not to keep any
circular reference around.
-
0e880d8f
by Elena at 2026-08-19T11:09:19+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Stop exposing ReplyFactory to the crate.
That should remain an internal type.
Instead, we can expose a "make_reply" test utility, to create one reply
at once. That will be enough.
-
491ab443
by Elena at 2026-08-19T11:09:19+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Do not add missing CRLF at the control port.
We will be building commands, no need to really include the logic to
add missing CRLF. Instead, catch malformed commands during development.
-
8a46afe3
by Elena at 2026-08-19T11:11:03+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Reorder some methods.
-
81b76447
by Elena at 2026-08-19T11:11:05+02:00
fixup! TB 44806: Implement the tor integration in Rust.
TB 44930: Implement the commands on the Rust control port
Remove ambiguity on the connection closed error.
12 changed files:
Changes:
toolkit/components/tor-integration/TorService.h
| ... |
... |
@@ -7,17 +7,17 @@ |
|
7
|
7
|
|
|
8
|
8
|
#include "nsCOMPtr.h"
|
|
9
|
9
|
|
|
10
|
|
-#include "ITorService.h"
|
|
|
10
|
+#include "torITorService.h"
|
|
11
|
11
|
|
|
12
|
12
|
// Inspired by
|
|
13
|
13
|
// toolkit/components/extensions/storage/ExtensionStorageComponents.h.
|
|
14
|
14
|
|
|
15
|
15
|
// Implemented in Rust
|
|
16
|
|
-extern "C" nsresult NewTorServiceImpl(ITorService** aResult);
|
|
|
16
|
+extern "C" nsresult NewTorServiceImpl(torITorService** aResult);
|
|
17
|
17
|
|
|
18
|
18
|
namespace torproject {
|
|
19
|
|
-already_AddRefed<ITorService> NewTorService() {
|
|
20
|
|
- nsCOMPtr<ITorService> service;
|
|
|
19
|
+already_AddRefed<torITorService> NewTorService() {
|
|
|
20
|
+ nsCOMPtr<torITorService> service;
|
|
21
|
21
|
nsresult rv = NewTorServiceImpl(getter_AddRefs(service));
|
|
22
|
22
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
23
|
23
|
return nullptr;
|
toolkit/components/tor-integration/components.conf
| ... |
... |
@@ -6,7 +6,7 @@ Classes = [ |
|
6
|
6
|
{
|
|
7
|
7
|
'cid': '{2537313c-e120-408c-a2f6-86e6e1bdf899}',
|
|
8
|
8
|
'contract_ids': ['@torproject.org/tor-service;1'],
|
|
9
|
|
- 'interfaces': ['ITorService'],
|
|
|
9
|
+ 'interfaces': ['torITorService'],
|
|
10
|
10
|
'constructor': 'torproject::NewTorService',
|
|
11
|
11
|
'headers': ['torproject/TorService.h'],
|
|
12
|
12
|
'js_name': 'tor',
|
toolkit/components/tor-integration/moz.build
| ... |
... |
@@ -3,7 +3,7 @@ |
|
3
|
3
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
4
|
|
|
5
|
5
|
XPIDL_MODULE = "tor"
|
|
6
|
|
-XPIDL_SOURCES += ["ITorService.idl"]
|
|
|
6
|
+XPIDL_SOURCES += ["torITorService.idl"]
|
|
7
|
7
|
XPCOM_MANIFESTS += [
|
|
8
|
8
|
"components.conf",
|
|
9
|
9
|
]
|
toolkit/components/tor-integration/ITorService.idl
→
toolkit/components/tor-integration/torITorService.idl
| ... |
... |
@@ -6,27 +6,27 @@ |
|
6
|
6
|
#include "nsIFile.idl"
|
|
7
|
7
|
|
|
8
|
8
|
[scriptable, uuid(4b250614-968a-412f-9191-9ffd9d4bd001)]
|
|
9
|
|
-interface ITorControlPortReceiver : nsISupports {
|
|
|
9
|
+interface torITorControlPortReceiver : nsISupports {
|
|
10
|
10
|
void onAsyncMessage(in ACString message);
|
|
11
|
11
|
};
|
|
12
|
12
|
|
|
13
|
13
|
[scriptable, uuid(1389d157-4695-43a2-a7d8-538aaa350766)]
|
|
14
|
|
-interface ITorMessageHandler : nsISupports {
|
|
|
14
|
+interface torITorMessageHandler : nsISupports {
|
|
15
|
15
|
void onMessage(in ACString message);
|
|
16
|
16
|
void onError(in ACString error);
|
|
17
|
17
|
};
|
|
18
|
18
|
|
|
19
|
19
|
// TODO: Remove once we have a provider.
|
|
20
|
20
|
[scriptable, uuid(f56d710e-5650-4b8c-8d31-279f360003ee)]
|
|
21
|
|
-interface ITorControlPort : nsISupports {
|
|
22
|
|
- void start(in ITorControlPortReceiver receiver);
|
|
23
|
|
- void sendCommand(in ACString command, in ITorMessageHandler handler);
|
|
|
21
|
+interface torITorControlPort : nsISupports {
|
|
|
22
|
+ void start(in torITorControlPortReceiver receiver);
|
|
|
23
|
+ void sendCommand(in ACString command, in torITorMessageHandler handler);
|
|
24
|
24
|
void close();
|
|
25
|
25
|
};
|
|
26
|
26
|
|
|
27
|
27
|
[scriptable, uuid(2537313c-e120-408c-a2f6-86e6e1bdf899)]
|
|
28
|
|
-interface ITorService : nsISupports {
|
|
|
28
|
+interface torITorService : nsISupports {
|
|
29
|
29
|
// For development purposes...
|
|
30
|
|
- ITorControlPort createControlPort(in ACString host, in long port);
|
|
31
|
|
- ITorControlPort createControlPortIPC(in nsIFile socket);
|
|
|
30
|
+ torITorControlPort createControlPort(in ACString host, in long port);
|
|
|
31
|
+ torITorControlPort createControlPortIPC(in nsIFile socket);
|
|
32
|
32
|
}; |
toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_port.rs
| ... |
... |
@@ -3,7 +3,7 @@ |
|
3
|
3
|
// <http://opensource.org/licenses/MIT>, at your option. This file may not be
|
|
4
|
4
|
// copied, modified, or distributed except according to those terms.
|
|
5
|
5
|
|
|
6
|
|
-use bytes::{BufMut, Bytes, BytesMut};
|
|
|
6
|
+use bytes::Bytes;
|
|
7
|
7
|
use std::{
|
|
8
|
8
|
cell::Cell,
|
|
9
|
9
|
rc::{Rc, Weak},
|
| ... |
... |
@@ -79,35 +79,22 @@ impl ControlPortInner { |
|
79
|
79
|
}
|
|
80
|
80
|
}
|
|
81
|
81
|
|
|
82
|
|
- fn close(&self) -> Result<(), ControlSocketError> {
|
|
83
|
|
- if self.closed.replace(true) {
|
|
84
|
|
- return Ok(());
|
|
85
|
|
- }
|
|
86
|
|
- self.reply_dispatcher.fail_all(ReplyError::ConnectionClosed);
|
|
87
|
|
- self.socket.close()
|
|
88
|
|
- }
|
|
89
|
|
-
|
|
90
|
|
- fn set_async_handler(&self, cb: Box<dyn Fn(Reply)>) {
|
|
91
|
|
- self.reply_dispatcher.set_async_handler(cb);
|
|
92
|
|
- }
|
|
93
|
|
-
|
|
94
|
82
|
fn send_command(
|
|
95
|
83
|
self: &Rc<Self>,
|
|
96
|
|
- mut command: Bytes,
|
|
|
84
|
+ command: Bytes,
|
|
97
|
85
|
handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>,
|
|
98
|
86
|
) {
|
|
99
|
87
|
if self.closed.get() {
|
|
100
|
|
- handler(Err(ControlPortError::ConnectionError(
|
|
101
|
|
- ControlSocketError::ConnectionClosed,
|
|
|
88
|
+ handler(Err(ControlPortError::ProtocolError(
|
|
|
89
|
+ ReplyError::ConnectionClosed,
|
|
102
|
90
|
)));
|
|
103
|
91
|
return;
|
|
104
|
92
|
}
|
|
105
|
93
|
|
|
106
|
|
- if !command.ends_with(b"\r\n") {
|
|
107
|
|
- let mut buf = BytesMut::from(command);
|
|
108
|
|
- buf.put(&b"\r\n"[..]);
|
|
109
|
|
- command = buf.freeze();
|
|
110
|
|
- }
|
|
|
94
|
+ debug_assert!(
|
|
|
95
|
+ command.ends_with(b"\r\n"),
|
|
|
96
|
+ "Commands are expected to end with CRLF."
|
|
|
97
|
+ );
|
|
111
|
98
|
|
|
112
|
99
|
// The callback is going to be called before other commands are sent,
|
|
113
|
100
|
// therefore it is safe to queue the callback at this point, as next
|
| ... |
... |
@@ -128,14 +115,33 @@ impl ControlPortInner { |
|
128
|
115
|
handler(r.map_err(|e| ControlPortError::ProtocolError(e)));
|
|
129
|
116
|
}));
|
|
130
|
117
|
}
|
|
131
|
|
- Err(e) => {
|
|
132
|
|
- handler(Err(ControlPortError::from(e)));
|
|
|
118
|
+ Err(ControlSocketError::ConnectionClosed) => {
|
|
|
119
|
+ handler(Err(ControlPortError::ProtocolError(
|
|
|
120
|
+ ReplyError::ConnectionClosed,
|
|
|
121
|
+ )));
|
|
|
122
|
+ this.async_failure();
|
|
|
123
|
+ }
|
|
|
124
|
+ Err(ControlSocketError::ImplementationError(rv)) => {
|
|
|
125
|
+ handler(Err(ControlPortError::ConnectionError(rv)));
|
|
133
|
126
|
this.async_failure();
|
|
134
|
127
|
}
|
|
135
|
128
|
}
|
|
136
|
129
|
}),
|
|
137
|
130
|
);
|
|
138
|
131
|
}
|
|
|
132
|
+
|
|
|
133
|
+ fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
|
|
|
134
|
+ self.reply_dispatcher.set_async_handler(cb);
|
|
|
135
|
+ }
|
|
|
136
|
+
|
|
|
137
|
+ fn close(&self) -> Result<(), ControlSocketError> {
|
|
|
138
|
+ if self.closed.replace(true) {
|
|
|
139
|
+ return Ok(());
|
|
|
140
|
+ }
|
|
|
141
|
+ self.reply_dispatcher.fail_all(ReplyError::ConnectionClosed);
|
|
|
142
|
+ self.reply_dispatcher.set_async_handler(None);
|
|
|
143
|
+ self.socket.close()
|
|
|
144
|
+ }
|
|
139
|
145
|
}
|
|
140
|
146
|
|
|
141
|
147
|
impl Drop for ControlPortInner {
|
| ... |
... |
@@ -157,11 +163,6 @@ impl ControlPort { |
|
157
|
163
|
Ok(Self(ControlPortInner::new(Rc::from(socket))?))
|
|
158
|
164
|
}
|
|
159
|
165
|
|
|
160
|
|
- #[inline]
|
|
161
|
|
- pub fn close(&self) -> Result<(), ControlSocketError> {
|
|
162
|
|
- self.0.close()
|
|
163
|
|
- }
|
|
164
|
|
-
|
|
165
|
166
|
// TODO: Keep only the methods speicifc to commands and remove this one
|
|
166
|
167
|
// (tor-browser#44930).
|
|
167
|
168
|
#[inline]
|
| ... |
... |
@@ -174,7 +175,12 @@ impl ControlPort { |
|
174
|
175
|
}
|
|
175
|
176
|
|
|
176
|
177
|
#[inline]
|
|
177
|
|
- pub fn set_async_handler(&self, cb: Box<dyn Fn(Reply)>) {
|
|
|
178
|
+ pub fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
|
|
178
|
179
|
self.0.set_async_handler(cb);
|
|
179
|
180
|
}
|
|
|
181
|
+
|
|
|
182
|
+ #[inline]
|
|
|
183
|
+ pub fn close(&self) -> Result<(), ControlSocketError> {
|
|
|
184
|
+ self.0.close()
|
|
|
185
|
+ }
|
|
180
|
186
|
} |
toolkit/components/tor-integration/tor_provider/src/ctor/control_port/error.rs
| ... |
... |
@@ -5,14 +5,12 @@ |
|
5
|
5
|
|
|
6
|
6
|
use thiserror::Error;
|
|
7
|
7
|
|
|
8
|
|
-use super::super::{ControlSocketError, ReplyError};
|
|
|
8
|
+use crate::ctor::ReplyError;
|
|
9
|
9
|
|
|
10
|
10
|
#[derive(Error, Debug, Clone, PartialEq, Eq)]
|
|
11
|
11
|
pub enum ControlPortError {
|
|
12
|
|
- #[error("connection error")]
|
|
13
|
|
- ConnectionError(#[from] ControlSocketError),
|
|
14
|
|
- #[error("protocol violation")]
|
|
|
12
|
+ #[error("connection error: {0:#x}")]
|
|
|
13
|
+ ConnectionError(u32),
|
|
|
14
|
+ #[error("protocol violation: {0}")]
|
|
15
|
15
|
ProtocolError(#[from] ReplyError),
|
|
16
|
|
- #[error("unsuccessful command ({code}): {message}")]
|
|
17
|
|
- TorError { code: u16, message: String },
|
|
18
|
16
|
} |
toolkit/components/tor-integration/tor_provider/src/ctor/reply_parser/dispatcher.rs
| ... |
... |
@@ -56,9 +56,9 @@ impl ReplyDispatcher { |
|
56
|
56
|
}
|
|
57
|
57
|
|
|
58
|
58
|
/// Register the handler for async notifications.
|
|
59
|
|
- pub fn set_async_handler(&self, cb: Box<dyn Fn(Reply)>) {
|
|
|
59
|
+ pub fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
|
|
60
|
60
|
// Reentrancy safety: we do not use the handler yet.
|
|
61
|
|
- *self.async_handler.borrow_mut() = Some(Rc::from(cb))
|
|
|
61
|
+ *self.async_handler.borrow_mut() = cb.map(|cb| Rc::from(cb));
|
|
62
|
62
|
}
|
|
63
|
63
|
|
|
64
|
64
|
/// Feed data to parse.
|
| ... |
... |
@@ -290,7 +290,7 @@ mod tests { |
|
290
|
290
|
let dispatcher = ReplyDispatcher::new();
|
|
291
|
291
|
let notification_called = Rc::new(Cell::new(false));
|
|
292
|
292
|
let notification_called2 = notification_called.clone();
|
|
293
|
|
- dispatcher.set_async_handler(Box::new(move |r| {
|
|
|
293
|
+ dispatcher.set_async_handler(Some(Box::new(move |r| {
|
|
294
|
294
|
assert_eq!(
|
|
295
|
295
|
*r.end_line(),
|
|
296
|
296
|
EndReplyLine {
|
| ... |
... |
@@ -299,7 +299,7 @@ mod tests { |
|
299
|
299
|
}
|
|
300
|
300
|
);
|
|
301
|
301
|
notification_called2.set(true);
|
|
302
|
|
- }));
|
|
|
302
|
+ })));
|
|
303
|
303
|
let reply_called = Rc::new(Cell::new(false));
|
|
304
|
304
|
let reply_called2 = reply_called.clone();
|
|
305
|
305
|
dispatcher.push_callback(Box::new(move |_| {
|
| ... |
... |
@@ -323,12 +323,12 @@ mod tests { |
|
323
|
323
|
#[test]
|
|
324
|
324
|
fn async_replace_handler() {
|
|
325
|
325
|
let dispatcher = ReplyDispatcher::new();
|
|
326
|
|
- dispatcher.set_async_handler(Box::new(move |_| {
|
|
|
326
|
+ dispatcher.set_async_handler(Some(Box::new(move |_| {
|
|
327
|
327
|
unreachable!("This handler should never be called, as it has been replaced.");
|
|
328
|
|
- }));
|
|
|
328
|
+ })));
|
|
329
|
329
|
let called = Rc::new(Cell::new(false));
|
|
330
|
330
|
let c = called.clone();
|
|
331
|
|
- dispatcher.set_async_handler(Box::new(move |r| {
|
|
|
331
|
+ dispatcher.set_async_handler(Some(Box::new(move |r| {
|
|
332
|
332
|
assert_eq!(
|
|
333
|
333
|
*r.end_line(),
|
|
334
|
334
|
EndReplyLine {
|
| ... |
... |
@@ -337,7 +337,7 @@ mod tests { |
|
337
|
337
|
}
|
|
338
|
338
|
);
|
|
339
|
339
|
c.set(true);
|
|
340
|
|
- }));
|
|
|
340
|
+ })));
|
|
341
|
341
|
assert!(!called.get());
|
|
342
|
342
|
dispatcher
|
|
343
|
343
|
.feed(&Bytes::from_static(b"650 Notification\r\n"))
|
| ... |
... |
@@ -345,6 +345,18 @@ mod tests { |
|
345
|
345
|
assert!(called.get());
|
|
346
|
346
|
}
|
|
347
|
347
|
|
|
|
348
|
+ #[test]
|
|
|
349
|
+ fn async_remove_handler() {
|
|
|
350
|
+ let dispatcher = ReplyDispatcher::new();
|
|
|
351
|
+ dispatcher.set_async_handler(Some(Box::new(move |_| {
|
|
|
352
|
+ unreachable!("This handler should never be called, as it has been replaced.");
|
|
|
353
|
+ })));
|
|
|
354
|
+ dispatcher.set_async_handler(None);
|
|
|
355
|
+ dispatcher
|
|
|
356
|
+ .feed(&Bytes::from_static(b"650 Notification\r\n"))
|
|
|
357
|
+ .unwrap();
|
|
|
358
|
+ }
|
|
|
359
|
+
|
|
348
|
360
|
#[test]
|
|
349
|
361
|
fn sync_async_single_feed() {
|
|
350
|
362
|
let dispatcher = ReplyDispatcher::new();
|
| ... |
... |
@@ -356,7 +368,7 @@ mod tests { |
|
356
|
368
|
}));
|
|
357
|
369
|
let async_called = Rc::new(Cell::new(false));
|
|
358
|
370
|
let ac = async_called.clone();
|
|
359
|
|
- dispatcher.set_async_handler(Box::new(move |r| {
|
|
|
371
|
+ dispatcher.set_async_handler(Some(Box::new(move |r| {
|
|
360
|
372
|
assert_eq!(
|
|
361
|
373
|
*r.end_line(),
|
|
362
|
374
|
EndReplyLine {
|
| ... |
... |
@@ -365,7 +377,7 @@ mod tests { |
|
365
|
377
|
}
|
|
366
|
378
|
);
|
|
367
|
379
|
ac.set(true);
|
|
368
|
|
- }));
|
|
|
380
|
+ })));
|
|
369
|
381
|
dispatcher
|
|
370
|
382
|
.feed(&Bytes::from_static(b"250 OK\r\n650 Notification\r\n"))
|
|
371
|
383
|
.unwrap();
|
| ... |
... |
@@ -450,13 +462,13 @@ mod tests { |
|
450
|
462
|
let notifications = Rc::new(Cell::new(0));
|
|
451
|
463
|
let n = notifications.clone();
|
|
452
|
464
|
let d = dispatcher.clone();
|
|
453
|
|
- dispatcher.set_async_handler(Box::new(move |_| {
|
|
|
465
|
+ dispatcher.set_async_handler(Some(Box::new(move |_| {
|
|
454
|
466
|
n.set(n.get() + 1);
|
|
455
|
467
|
if n.get() < 2 {
|
|
456
|
468
|
d.feed(&Bytes::from_static(b"650 Notification 2\r\n"))
|
|
457
|
469
|
.unwrap();
|
|
458
|
470
|
}
|
|
459
|
|
- }));
|
|
|
471
|
+ })));
|
|
460
|
472
|
dispatcher
|
|
461
|
473
|
.feed(&Bytes::from_static(b"650 Notification 1\r\n"))
|
|
462
|
474
|
.unwrap();
|
| ... |
... |
@@ -514,9 +526,9 @@ mod tests { |
|
514
|
526
|
#[test]
|
|
515
|
527
|
fn error_propagation() {
|
|
516
|
528
|
let dispatcher = ReplyDispatcher::new();
|
|
517
|
|
- dispatcher.set_async_handler(Box::new(|_| {
|
|
|
529
|
+ dispatcher.set_async_handler(Some(Box::new(|_| {
|
|
518
|
530
|
unreachable!("Errors should not be propagated to the async handler.");
|
|
519
|
|
- }));
|
|
|
531
|
+ })));
|
|
520
|
532
|
let first = Rc::new(Cell::new(false));
|
|
521
|
533
|
let f = first.clone();
|
|
522
|
534
|
let second = Rc::new(Cell::new(false));
|
toolkit/components/tor-integration/tor_provider/src/ctor/reply_parser/mod.rs
| ... |
... |
@@ -15,6 +15,8 @@ mod test_utils; |
|
15
|
15
|
|
|
16
|
16
|
pub use dispatcher::ReplyDispatcher;
|
|
17
|
17
|
pub use error::ReplyError;
|
|
18
|
|
-pub(crate) use factory::ReplyFactory;
|
|
19
|
18
|
pub use line::{DetailReplyLine, EndReplyLine};
|
|
20
|
19
|
pub use reply::Reply;
|
|
|
20
|
+
|
|
|
21
|
+#[cfg(test)]
|
|
|
22
|
+pub(crate) use test_utils::make_reply; |
toolkit/components/tor-integration/tor_provider/src/ctor/reply_parser/test_utils.rs
| ... |
... |
@@ -6,10 +6,19 @@ |
|
6
|
6
|
use bytes::Bytes;
|
|
7
|
7
|
|
|
8
|
8
|
use super::{
|
|
|
9
|
+ factory::ReplyFactory,
|
|
9
|
10
|
line::{DetailReplyLine, EndReplyLine},
|
|
10
|
11
|
reply::Reply,
|
|
11
|
12
|
};
|
|
12
|
13
|
|
|
|
14
|
+pub(crate) fn make_reply(data: &'static [u8]) -> Reply {
|
|
|
15
|
+ let mut factory = ReplyFactory::default();
|
|
|
16
|
+ let mut replies = factory.build(&Bytes::from_static(data)).unwrap();
|
|
|
17
|
+ assert_eq!(replies.len(), 1);
|
|
|
18
|
+ assert!(!factory.has_pending_data());
|
|
|
19
|
+ replies.pop_front().unwrap()
|
|
|
20
|
+}
|
|
|
21
|
+
|
|
13
|
22
|
pub(super) fn check_250_ok(reply: &Reply, has_details: bool) {
|
|
14
|
23
|
assert_eq!(reply.has_details(), has_details);
|
|
15
|
24
|
assert_eq!(
|
toolkit/components/tor-integration/tor_service/src/control_port.rs
| ... |
... |
@@ -2,18 +2,20 @@ |
|
2
|
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
3
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4
|
4
|
|
|
5
|
|
-use bytes::Bytes;
|
|
6
|
5
|
use nserror::nsresult;
|
|
7
|
6
|
use nserror::{NS_ERROR_NOT_CONNECTED, NS_OK};
|
|
8
|
7
|
use nsstring::{nsACString, nsCString};
|
|
9
|
|
-use tor_provider::ctor::ControlPort;
|
|
10
|
|
-use tor_provider::ctor::ControlSocketError;
|
|
11
|
|
-use xpcom::interfaces::{nsIFile, ITorControlPort, ITorControlPortReceiver, ITorMessageHandler};
|
|
|
8
|
+use tor_provider::ctor::{ControlPort, ControlSocketError};
|
|
|
9
|
+use xpcom::interfaces::{nsIFile, torITorControlPortReceiver, torITorMessageHandler};
|
|
12
|
10
|
use xpcom::RefPtr;
|
|
13
|
11
|
|
|
|
12
|
+// Actually used, but the compiler does not detect it.
|
|
|
13
|
+#[allow(unused)]
|
|
|
14
|
+use xpcom::interfaces::torITorControlPort;
|
|
|
15
|
+
|
|
14
|
16
|
use super::control_socket::ControlSocketXpcom;
|
|
15
|
17
|
|
|
16
|
|
-#[xpcom(implement(ITorControlPort), atomic)]
|
|
|
18
|
+#[xpcom(implement(torITorControlPort), atomic)]
|
|
17
|
19
|
pub struct ControlPortXpcom {
|
|
18
|
20
|
control_port: ControlPort,
|
|
19
|
21
|
}
|
| ... |
... |
@@ -32,42 +34,46 @@ impl ControlPortXpcom { |
|
32
|
34
|
Ok(Self::allocate(InitControlPortXpcom { control_port }))
|
|
33
|
35
|
}
|
|
34
|
36
|
|
|
35
|
|
- xpcom_method!(start => Start(receiver: *const ITorControlPortReceiver));
|
|
36
|
|
- pub fn start(&self, receiver: &ITorControlPortReceiver) -> Result<(), nsresult> {
|
|
|
37
|
+ xpcom_method!(start => Start(receiver: *const torITorControlPortReceiver));
|
|
|
38
|
+ pub fn start(&self, receiver: &torITorControlPortReceiver) -> Result<(), nsresult> {
|
|
37
|
39
|
let receiver = RefPtr::new(receiver);
|
|
38
|
|
- self.control_port.set_async_handler(Box::new(move |reply| {
|
|
39
|
|
- let mut buf = Vec::new();
|
|
40
|
|
- if let Err(e) = reply.write_to(&mut buf) {
|
|
41
|
|
- log::error!(
|
|
42
|
|
- "Cannot convert the reply to the raw message: {}.",
|
|
43
|
|
- e.to_string()
|
|
44
|
|
- );
|
|
45
|
|
- return;
|
|
46
|
|
- }
|
|
47
|
|
- if buf.ends_with(b"\r\n") {
|
|
48
|
|
- buf.truncate(buf.len() - 2);
|
|
49
|
|
- }
|
|
50
|
|
- // These conversions re-use the buffer, Gecko uses the same
|
|
51
|
|
- // allocator for Rust and C++ (see the nsstring crate).
|
|
52
|
|
- let as_str = nsCString::from(buf);
|
|
53
|
|
- // Safety: call to an XPCOM method that we expect to be exposed on
|
|
54
|
|
- // Rust. As per the documentation in nsstring, it is safe to pass
|
|
55
|
|
- // nsCStrings created in Rust to C++.
|
|
56
|
|
- unsafe { receiver.OnAsyncMessage(&*as_str) };
|
|
57
|
|
- }));
|
|
|
40
|
+ self.control_port
|
|
|
41
|
+ .set_async_handler(Some(Box::new(move |reply| {
|
|
|
42
|
+ let mut buf = Vec::new();
|
|
|
43
|
+ if let Err(e) = reply.write_to(&mut buf) {
|
|
|
44
|
+ log::error!(
|
|
|
45
|
+ "Cannot convert the reply to the raw message: {}.",
|
|
|
46
|
+ e.to_string()
|
|
|
47
|
+ );
|
|
|
48
|
+ return;
|
|
|
49
|
+ }
|
|
|
50
|
+ if buf.ends_with(b"\r\n") {
|
|
|
51
|
+ buf.truncate(buf.len() - 2);
|
|
|
52
|
+ }
|
|
|
53
|
+ // These conversions re-use the buffer, Gecko uses the same
|
|
|
54
|
+ // allocator for Rust and C++ (see the nsstring crate).
|
|
|
55
|
+ let as_str = nsCString::from(buf);
|
|
|
56
|
+ // Safety: call to an XPCOM method that we expect to be exposed
|
|
|
57
|
+ // on Rust. As per the documentation in nsstring, it is safe to
|
|
|
58
|
+ // pass nsCStrings created in Rust to C++.
|
|
|
59
|
+ unsafe { receiver.OnAsyncMessage(&*as_str) };
|
|
|
60
|
+ })));
|
|
58
|
61
|
Ok(())
|
|
59
|
62
|
}
|
|
60
|
63
|
|
|
61
|
|
- xpcom_method!(send_command => SendCommand(command: *const nsACString, handler: *const ITorMessageHandler));
|
|
|
64
|
+ xpcom_method!(send_command => SendCommand(command: *const nsACString, handler: *const torITorMessageHandler));
|
|
62
|
65
|
pub fn send_command(
|
|
63
|
66
|
&self,
|
|
64
|
67
|
command: &nsACString,
|
|
65
|
|
- handler: &ITorMessageHandler,
|
|
|
68
|
+ handler: &torITorMessageHandler,
|
|
66
|
69
|
) -> Result<(), nsresult> {
|
|
67
|
|
- let command = Bytes::copy_from_slice(&command[..]);
|
|
|
70
|
+ let mut command = command.to_vec();
|
|
|
71
|
+ if !command.ends_with(b"\r\n") {
|
|
|
72
|
+ command.extend_from_slice(b"\r\n");
|
|
|
73
|
+ }
|
|
68
|
74
|
let handler = RefPtr::new(handler);
|
|
69
|
75
|
self.control_port.send_command(
|
|
70
|
|
- command,
|
|
|
76
|
+ command.into(),
|
|
71
|
77
|
Box::new(move |reply| {
|
|
72
|
78
|
let mut buf = Vec::new();
|
|
73
|
79
|
let reply = match reply {
|
toolkit/components/tor-integration/tor_service/src/service.rs
| ... |
... |
@@ -4,12 +4,16 @@ |
|
4
|
4
|
|
|
5
|
5
|
use nserror::{nsresult, NS_ERROR_NULL_POINTER, NS_OK};
|
|
6
|
6
|
use nsstring::nsACString;
|
|
7
|
|
-use xpcom::interfaces::{nsIFile, ITorControlPort, ITorService};
|
|
|
7
|
+use xpcom::interfaces::{nsIFile, torITorControlPort};
|
|
8
|
8
|
use xpcom::RefPtr;
|
|
9
|
9
|
|
|
10
|
10
|
use super::control_port::ControlPortXpcom;
|
|
11
|
11
|
|
|
12
|
|
-#[xpcom(implement(ITorService), atomic)]
|
|
|
12
|
+// Actually used, but the compiler does not detect it.
|
|
|
13
|
+#[allow(unused)]
|
|
|
14
|
+use xpcom::interfaces::torITorService;
|
|
|
15
|
+
|
|
|
16
|
+#[xpcom(implement(torITorService), atomic)]
|
|
13
|
17
|
struct TorService {}
|
|
14
|
18
|
|
|
15
|
19
|
impl TorService {
|
| ... |
... |
@@ -17,31 +21,31 @@ impl TorService { |
|
17
|
21
|
TorService::allocate(InitTorService {})
|
|
18
|
22
|
}
|
|
19
|
23
|
|
|
20
|
|
- xpcom_method!(create_control_port => CreateControlPort(host: *const nsACString, port: i32, out: *mut *const ITorControlPort));
|
|
|
24
|
+ xpcom_method!(create_control_port => CreateControlPort(host: *const nsACString, port: i32, out: *mut *const torITorControlPort));
|
|
21
|
25
|
fn create_control_port(
|
|
22
|
26
|
&self,
|
|
23
|
27
|
host: &nsACString,
|
|
24
|
28
|
port: i32,
|
|
25
|
|
- out: *mut *const ITorControlPort,
|
|
|
29
|
+ out: *mut *const torITorControlPort,
|
|
26
|
30
|
) -> Result<(), nsresult> {
|
|
27
|
31
|
if out.is_null() {
|
|
28
|
32
|
return Err(NS_ERROR_NULL_POINTER);
|
|
29
|
33
|
}
|
|
30
|
|
- let cp = RefPtr::new(ControlPortXpcom::new_tcp(host, port)?.coerce::<ITorControlPort>());
|
|
|
34
|
+ let cp = RefPtr::new(ControlPortXpcom::new_tcp(host, port)?.coerce::<torITorControlPort>());
|
|
31
|
35
|
cp.forget(unsafe { &mut *out });
|
|
32
|
36
|
Ok(())
|
|
33
|
37
|
}
|
|
34
|
38
|
|
|
35
|
|
- xpcom_method!(create_control_port_ipc => CreateControlPortIPC(socket: *const nsIFile, out: *mut *const ITorControlPort));
|
|
|
39
|
+ xpcom_method!(create_control_port_ipc => CreateControlPortIPC(socket: *const nsIFile, out: *mut *const torITorControlPort));
|
|
36
|
40
|
fn create_control_port_ipc(
|
|
37
|
41
|
&self,
|
|
38
|
42
|
socket: &nsIFile,
|
|
39
|
|
- out: *mut *const ITorControlPort,
|
|
|
43
|
+ out: *mut *const torITorControlPort,
|
|
40
|
44
|
) -> Result<(), nsresult> {
|
|
41
|
45
|
if out.is_null() {
|
|
42
|
46
|
return Err(NS_ERROR_NULL_POINTER);
|
|
43
|
47
|
}
|
|
44
|
|
- let cp = RefPtr::new(ControlPortXpcom::new_ipc(socket)?.coerce::<ITorControlPort>());
|
|
|
48
|
+ let cp = RefPtr::new(ControlPortXpcom::new_ipc(socket)?.coerce::<torITorControlPort>());
|
|
45
|
49
|
cp.forget(unsafe { &mut *out });
|
|
46
|
50
|
Ok(())
|
|
47
|
51
|
}
|
| ... |
... |
@@ -49,11 +53,11 @@ impl TorService { |
|
49
|
53
|
|
|
50
|
54
|
// See toolkit/components/extensions/storage/webext_storage_bridge/src/lib.rs.
|
|
51
|
55
|
#[no_mangle]
|
|
52
|
|
-pub unsafe extern "C" fn NewTorServiceImpl(result: *mut *const ITorService) -> nsresult {
|
|
|
56
|
+pub unsafe extern "C" fn NewTorServiceImpl(result: *mut *const torITorService) -> nsresult {
|
|
53
|
57
|
if result.is_null() {
|
|
54
|
58
|
return NS_ERROR_NULL_POINTER;
|
|
55
|
59
|
}
|
|
56
|
60
|
let service = TorService::new();
|
|
57
|
|
- RefPtr::new(service.coerce::<ITorService>()).forget(&mut *result);
|
|
|
61
|
+ RefPtr::new(service.coerce::<torITorService>()).forget(&mut *result);
|
|
58
|
62
|
NS_OK
|
|
59
|
63
|
} |
tools/lint/eslint/eslint-plugin-mozilla/lib/services.json
| ... |
... |
@@ -59,5 +59,5 @@ |
|
59
|
59
|
"nsIXULAppInfo": "appinfo",
|
|
60
|
60
|
"nsIXULRuntime": "appinfo",
|
|
61
|
61
|
"nsIXULStore": "xulStore",
|
|
62
|
|
- "ITorService": "tor"
|
|
|
62
|
+ "torITorService": "tor"
|
|
63
|
63
|
} |
|