[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-153.2.0esr-16.0-1] 3 commits: fixup! TB 44806: Implement the tor integration in Rust.



Title: GitLab

brizental pushed to branch tor-browser-153.2.0esr-16.0-1 at The Tor Project / Applications / Tor Browser

Commits:

  • 6752dfdd
    by Elena at 2026-09-09T22:34:24+02:00
    fixup! TB 44806: Implement the tor integration in Rust.
    
    TB 44930: Implement the commands on the Rust control port
    
    Created TorController
    
  • 14c31b41
    by Elena at 2026-09-09T22:34:24+02:00
    fixup! TB 44806: Implement the tor integration in Rust.
    
    TB 44930: Implement the commands on the Rust control port
    
    Implemented the authenticate method.
    
  • 304b6706
    by Elena at 2026-09-09T22:34:24+02:00
    fixup! TB 44806: Implement the tor integration in Rust.
    
    TB 44930: Implement the commands on the Rust control port
    
    Implemented a few simple commands.
    

23 changed files:

Changes:

  • Cargo.lock
    ... ... @@ -8122,6 +8122,7 @@ name = "tor_provider"
    8122 8122
     version = "0.1.0"
    
    8123 8123
     dependencies = [
    
    8124 8124
      "bytes",
    
    8125
    + "hex",
    
    8125 8126
      "log",
    
    8126 8127
      "memchr",
    
    8127 8128
      "thiserror 2.0.12",
    

  • toolkit/components/tor-integration/tor_provider/Cargo.toml
    ... ... @@ -6,6 +6,7 @@ edition = "2021"
    6 6
     
    
    7 7
     [dependencies]
    
    8 8
     bytes = "1.4.0"
    
    9
    +hex = "0.4.3"
    
    9 10
     log = "0.4"
    
    10 11
     memchr = "2.7.4"
    
    11 12
     thiserror = "2"

  • toolkit/components/tor-integration/tor_provider/src/ctor/control_port/control_port.rs
    ... ... @@ -15,8 +15,20 @@ use super::{
    15 15
         error::ControlPortError,
    
    16 16
         message_pump::{MessagePump, ReadAction},
    
    17 17
     };
    
    18
    +
    
    18 19
     use crate::ctor::reply_parser::{Reply, ReplyDispatcher, ReplyError};
    
    19 20
     
    
    21
    +pub trait ControlPortInterface {
    
    22
    +    fn send_command(
    
    23
    +        &self,
    
    24
    +        command: Bytes,
    
    25
    +        handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>,
    
    26
    +    );
    
    27
    +    fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>);
    
    28
    +    fn close(&self) -> Result<(), ControlSocketError>;
    
    29
    +    fn set_close_handler(&self, cb: Box<dyn FnOnce()>);
    
    30
    +}
    
    31
    +
    
    20 32
     /// The lower-level part of the control port implementation.
    
    21 33
     /// It contains the logic for actually sending the command, and it hides its
    
    22 34
     /// reference-counted nature from actual consumers.
    
    ... ... @@ -172,6 +184,9 @@ impl Drop for ControlPortInner {
    172 184
         }
    
    173 185
     }
    
    174 186
     
    
    187
    +// Wrap the inner type to make it clearer that its ownership is not supposed to
    
    188
    +// be shared even though it uses an Rc, but the use of the Rc is due to the
    
    189
    +// callback structure.
    
    175 190
     pub struct ControlPort(Rc<ControlPortInner>);
    
    176 191
     
    
    177 192
     impl ControlPort {
    
    ... ... @@ -179,11 +194,11 @@ impl ControlPort {
    179 194
         pub fn new(socket: Box<dyn ControlSocket>) -> Result<Self, ControlSocketError> {
    
    180 195
             Ok(Self(ControlPortInner::new(Rc::from(socket))?))
    
    181 196
         }
    
    197
    +}
    
    182 198
     
    
    183
    -    // TODO: Keep only the methods speicifc to commands and remove this one
    
    184
    -    // (tor-browser#44930).
    
    199
    +impl ControlPortInterface for ControlPort {
    
    185 200
         #[inline]
    
    186
    -    pub fn send_command(
    
    201
    +    fn send_command(
    
    187 202
             &self,
    
    188 203
             command: Bytes,
    
    189 204
             handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>,
    
    ... ... @@ -192,17 +207,17 @@ impl ControlPort {
    192 207
         }
    
    193 208
     
    
    194 209
         #[inline]
    
    195
    -    pub fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
    
    210
    +    fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
    
    196 211
             self.0.set_async_handler(cb);
    
    197 212
         }
    
    198 213
     
    
    199 214
         #[inline]
    
    200
    -    pub fn close(&self) -> Result<(), ControlSocketError> {
    
    215
    +    fn close(&self) -> Result<(), ControlSocketError> {
    
    201 216
             self.0.close()
    
    202 217
         }
    
    203 218
     
    
    204 219
         #[inline]
    
    205
    -    pub fn set_close_handler(&self, cb: Box<dyn FnOnce()>) {
    
    220
    +    fn set_close_handler(&self, cb: Box<dyn FnOnce()>) {
    
    206 221
             self.0.set_close_handler(cb);
    
    207 222
         }
    
    208 223
     }

  • toolkit/components/tor-integration/tor_provider/src/ctor/control_port/mod.rs
    ... ... @@ -9,6 +9,6 @@ mod control_socket;
    9 9
     mod error;
    
    10 10
     mod message_pump;
    
    11 11
     
    
    12
    -pub use control_port::ControlPort;
    
    12
    +pub use control_port::*;
    
    13 13
     pub use control_socket::*;
    
    14 14
     pub use error::ControlPortError;

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/authenticate.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn authenticate(password: &[u8]) -> Result<Command<u16>, ControllerError> {
    
    10
    +    const COMMAND: &str = "AUTHENTICATE";
    
    11
    +    let mut command = String::new();
    
    12
    +    command.reserve(COMMAND.len() + 1 + password.len() * 2 + 2);
    
    13
    +    command.push_str(COMMAND);
    
    14
    +    if !password.is_empty() {
    
    15
    +        command.push(' ');
    
    16
    +        command.push_str(hex::encode(password).as_str());
    
    17
    +    }
    
    18
    +    command.push_str("\r\n");
    
    19
    +    Ok(Command {
    
    20
    +        command,
    
    21
    +        handler: Box::new(parse_ack),
    
    22
    +    })
    
    23
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/command.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use crate::ctor::{controller::ControllerError, reply_parser::Reply};
    
    7
    +
    
    8
    +pub struct Command<T> {
    
    9
    +    pub command: String,
    
    10
    +    pub handler: Box<dyn Fn(Reply) -> Result<T, ControllerError>>,
    
    11
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/mod.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +mod authenticate;
    
    7
    +mod command;
    
    8
    +mod reset_conf;
    
    9
    +mod save_conf;
    
    10
    +mod set_events;
    
    11
    +mod signal;
    
    12
    +mod take_ownership;
    
    13
    +
    
    14
    +pub use authenticate::authenticate;
    
    15
    +pub use command::Command;
    
    16
    +pub use reset_conf::reset_owning_controller_process;
    
    17
    +pub use save_conf::save_conf;
    
    18
    +pub use set_events::set_events;
    
    19
    +pub use signal::signal_newnym;
    
    20
    +pub use take_ownership::take_ownership;

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/reset_conf.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn reset_owning_controller_process() -> Result<Command<u16>, ControllerError> {
    
    10
    +    Ok(Command {
    
    11
    +        command: String::from("RESETCONF __OwningControllerProcess\r\n"),
    
    12
    +        handler: Box::new(parse_ack),
    
    13
    +    })
    
    14
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/save_conf.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn save_conf() -> Result<Command<u16>, ControllerError> {
    
    10
    +    Ok(Command {
    
    11
    +        command: String::from("SAVECONF\r\n"),
    
    12
    +        handler: Box::new(parse_ack),
    
    13
    +    })
    
    14
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/set_events.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn set_events(events: &[&str]) -> Result<Command<u16>, ControllerError> {
    
    10
    +    let mut command = String::from("SETEVENTS");
    
    11
    +    for e in events {
    
    12
    +        command.push(' ');
    
    13
    +        command.push_str(e);
    
    14
    +    }
    
    15
    +    command.push_str("\r\n");
    
    16
    +    Ok(Command {
    
    17
    +        command,
    
    18
    +        handler: Box::new(parse_ack),
    
    19
    +    })
    
    20
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/signal.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn signal_newnym() -> Result<Command<u16>, ControllerError> {
    
    10
    +    Ok(Command {
    
    11
    +        command: String::from("SIGNAL NEWNYM\r\n"),
    
    12
    +        handler: Box::new(parse_ack),
    
    13
    +    })
    
    14
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/commands/take_ownership.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::Command;
    
    7
    +use crate::ctor::controller::{parsers::parse_ack, ControllerError};
    
    8
    +
    
    9
    +pub fn take_ownership() -> Result<Command<u16>, ControllerError> {
    
    10
    +    Ok(Command {
    
    11
    +        command: String::from("TAKEOWNERSHIP\r\n"),
    
    12
    +        handler: Box::new(parse_ack),
    
    13
    +    })
    
    14
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/controller.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use bytes::Bytes;
    
    7
    +
    
    8
    +use super::{
    
    9
    +    commands::{self, Command},
    
    10
    +    error::ControllerError,
    
    11
    +};
    
    12
    +use crate::ctor::{
    
    13
    +    control_port::{ControlPortInterface, ControlSocketError},
    
    14
    +    reply_parser::Reply,
    
    15
    +};
    
    16
    +
    
    17
    +/// A controller for a tor daemon.
    
    18
    +///
    
    19
    +/// TorController wraps an implementation of a ControlPortInterface (the lower-level transport that
    
    20
    +/// exposes a command-based interface) and exposes a set of async, callback-based methods.
    
    21
    +/// Every method correspond to a control port command.
    
    22
    +///
    
    23
    +/// Callers should authenticate before issuing any other command, even when authentication is
    
    24
    +/// disabled on the server side, and close the connection when done with it.
    
    25
    +/// The tor daemon can be configured so that this controller is its owner, so that when it closes
    
    26
    +/// the connection, the remote daemon shuts down.
    
    27
    +pub struct TorController<CP: ControlPortInterface>(CP);
    
    28
    +
    
    29
    +impl<CP: ControlPortInterface> TorController<CP> {
    
    30
    +    #[inline]
    
    31
    +    pub fn new(control_port: CP) -> Self {
    
    32
    +        Self(control_port)
    
    33
    +    }
    
    34
    +
    
    35
    +    #[inline]
    
    36
    +    pub fn close(&self) -> Result<(), ControllerError> {
    
    37
    +        match self.0.close() {
    
    38
    +            Ok(()) => Ok(()),
    
    39
    +            Err(ControlSocketError::ConnectionClosed) => Ok(()),
    
    40
    +            Err(ControlSocketError::ImplementationError(rv)) => {
    
    41
    +                Err(ControllerError::ConnectionError(rv))
    
    42
    +            }
    
    43
    +        }
    
    44
    +    }
    
    45
    +
    
    46
    +    fn send_command<T>(
    
    47
    +        &self,
    
    48
    +        command: Result<Command<T>, ControllerError>,
    
    49
    +        handler: Box<dyn FnOnce(Result<T, ControllerError>)>,
    
    50
    +    ) where
    
    51
    +        T: 'static,
    
    52
    +    {
    
    53
    +        let Command {
    
    54
    +            command,
    
    55
    +            handler: command_handler,
    
    56
    +        } = match command {
    
    57
    +            Ok(c) => c,
    
    58
    +            Err(e) => {
    
    59
    +                handler(Err(e));
    
    60
    +                return;
    
    61
    +            }
    
    62
    +        };
    
    63
    +        self.0.send_command(
    
    64
    +            command.into(),
    
    65
    +            Box::new(move |r| match r {
    
    66
    +                Ok(reply) => handler(command_handler(reply)),
    
    67
    +                Err(e) => handler(Err(e.into())),
    
    68
    +            }),
    
    69
    +        );
    
    70
    +    }
    
    71
    +
    
    72
    +    // Setup
    
    73
    +
    
    74
    +    /// Authenticate to the tor daemon.
    
    75
    +    /// Notice that a failure in the authentication makes the connection close.
    
    76
    +    pub fn authenticate(
    
    77
    +        &self,
    
    78
    +        password: &[u8],
    
    79
    +        handler: Box<dyn FnOnce(Result<u16, ControllerError>)>,
    
    80
    +    ) {
    
    81
    +        self.send_command(commands::authenticate(password), handler);
    
    82
    +    }
    
    83
    +
    
    84
    +    pub fn take_ownership(&self, handler: Box<dyn FnOnce(Result<u16, ControllerError>)>) {
    
    85
    +        self.send_command(commands::take_ownership(), handler);
    
    86
    +    }
    
    87
    +
    
    88
    +    pub fn reset_owning_controller_process(
    
    89
    +        &self,
    
    90
    +        handler: Box<dyn FnOnce(Result<u16, ControllerError>)>,
    
    91
    +    ) {
    
    92
    +        self.send_command(commands::reset_owning_controller_process(), handler);
    
    93
    +    }
    
    94
    +
    
    95
    +    pub fn set_events(
    
    96
    +        &self,
    
    97
    +        events: &[&str],
    
    98
    +        handler: Box<dyn FnOnce(Result<u16, ControllerError>)>,
    
    99
    +    ) {
    
    100
    +        self.send_command(commands::set_events(events), handler);
    
    101
    +    }
    
    102
    +
    
    103
    +    // Connection management
    
    104
    +
    
    105
    +    pub fn save_conf(&self, handler: Box<dyn FnOnce(Result<u16, ControllerError>)>) {
    
    106
    +        self.send_command(commands::save_conf(), handler);
    
    107
    +    }
    
    108
    +
    
    109
    +    // Circuit display
    
    110
    +
    
    111
    +    // Onion authentication
    
    112
    +
    
    113
    +    // Miscellaneous
    
    114
    +
    
    115
    +    pub fn signal_newnym(&self, handler: Box<dyn FnOnce(Result<u16, ControllerError>)>) {
    
    116
    +        self.send_command(commands::signal_newnym(), handler);
    
    117
    +    }
    
    118
    +}
    
    119
    +
    
    120
    +// TODO: Remove once we merge all parts of tor-browser#44930.
    
    121
    +impl<CP: ControlPortInterface> TorController<CP> {
    
    122
    +    #[inline]
    
    123
    +    pub fn send_raw_command(
    
    124
    +        &self,
    
    125
    +        command: Bytes,
    
    126
    +        handler: Box<dyn FnOnce(Result<Reply, ControllerError>)>,
    
    127
    +    ) {
    
    128
    +        self.0.send_command(
    
    129
    +            command,
    
    130
    +            Box::new(|res| handler(res.map_err(|e| ControllerError::from(e)))),
    
    131
    +        );
    
    132
    +    }
    
    133
    +
    
    134
    +    #[inline]
    
    135
    +    pub fn set_async_handler(&self, cb: Option<Box<dyn Fn(Reply)>>) {
    
    136
    +        self.0.set_async_handler(cb);
    
    137
    +    }
    
    138
    +
    
    139
    +    #[inline]
    
    140
    +    pub fn set_close_handler(&self, cb: Box<dyn FnOnce()>) {
    
    141
    +        self.0.set_close_handler(cb);
    
    142
    +    }
    
    143
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/error.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use thiserror::Error;
    
    7
    +
    
    8
    +use crate::ctor::{
    
    9
    +    control_port::ControlPortError,
    
    10
    +    reply_parser::{Reply, ReplyError},
    
    11
    +};
    
    12
    +
    
    13
    +#[derive(Error, Debug, Clone, PartialEq, Eq)]
    
    14
    +pub enum ControllerError {
    
    15
    +    #[error("connection error: {0:#x}")]
    
    16
    +    ConnectionError(u32),
    
    17
    +    #[error("protocol violation: {0}")]
    
    18
    +    ProtocolError(#[from] ReplyError),
    
    19
    +    #[error("unsuccessful command ({code}): {message}")]
    
    20
    +    TorError {
    
    21
    +        code: u16,
    
    22
    +        // Notice: tor does not dive any guarantee about charsets.
    
    23
    +        // However, we expect errors to be ASCII strings, and they are used only
    
    24
    +        // for logs, they will never be directly user-facing.
    
    25
    +        message: String,
    
    26
    +    },
    
    27
    +    #[error("the reply contains lines with mixed codes")]
    
    28
    +    MixedCodes,
    
    29
    +    #[error("the reply does not match the expected format: {0}")]
    
    30
    +    WrongFormat(String),
    
    31
    +    #[error("the requested key {0} was not found")]
    
    32
    +    KeyNotFound(String),
    
    33
    +    #[error("malformed reply: {0}")]
    
    34
    +    MalformedReply(String),
    
    35
    +}
    
    36
    +
    
    37
    +impl ControllerError {
    
    38
    +    pub(super) fn from_reply(reply: &Reply) -> Option<Self> {
    
    39
    +        if reply.end_line().is_error() {
    
    40
    +            Some(Self::TorError {
    
    41
    +                code: reply.end_line().code,
    
    42
    +                message: String::from_utf8_lossy(&reply.end_line().line).into_owned(),
    
    43
    +            })
    
    44
    +        } else {
    
    45
    +            None
    
    46
    +        }
    
    47
    +    }
    
    48
    +}
    
    49
    +
    
    50
    +impl From<ControlPortError> for ControllerError {
    
    51
    +    fn from(value: ControlPortError) -> Self {
    
    52
    +        match value {
    
    53
    +            ControlPortError::ConnectionError(rv) => ControllerError::ConnectionError(rv),
    
    54
    +            ControlPortError::ProtocolError(err) => ControllerError::ProtocolError(err),
    
    55
    +        }
    
    56
    +    }
    
    57
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/mod.rs
    ... ... @@ -3,47 +3,10 @@
    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
    -mod escape;
    
    7
    -mod unescape;
    
    6
    +mod commands;
    
    7
    +mod controller;
    
    8
    +mod error;
    
    9
    +mod parsers;
    
    8 10
     
    
    9
    -use escape::*;
    
    10
    -use unescape::*;
    
    11
    -
    
    12
    -#[cfg(test)]
    
    13
    -mod tests {
    
    14
    -    use super::*;
    
    15
    -
    
    16
    -    fn round_trip(buf: &[u8]) {
    
    17
    -        let mut escaped = String::new();
    
    18
    -        tor_escape_into(buf, &mut escaped);
    
    19
    -        assert_eq!(&*tor_unescape(escaped.as_bytes()).unwrap(), buf);
    
    20
    -    }
    
    21
    -
    
    22
    -    #[test]
    
    23
    -    fn round_trip_simple() {
    
    24
    -        round_trip(b"test");
    
    25
    -    }
    
    26
    -
    
    27
    -    #[test]
    
    28
    -    fn round_trip_empty() {
    
    29
    -        round_trip(b"");
    
    30
    -    }
    
    31
    -
    
    32
    -    #[test]
    
    33
    -    fn round_trip_special_chars() {
    
    34
    -        round_trip(b"'\"\\\r\n\t");
    
    35
    -    }
    
    36
    -
    
    37
    -    #[test]
    
    38
    -    fn round_trip_non_utf8() {
    
    39
    -        round_trip(b"\xF5\xFF\xFE");
    
    40
    -    }
    
    41
    -
    
    42
    -    #[test]
    
    43
    -    fn round_trip_all_bytes() {
    
    44
    -        // Every possible byte value, escaped then unescaped,
    
    45
    -        // must come back unchanged.
    
    46
    -        let all_bytes: Vec<u8> = (0..=255).collect();
    
    47
    -        round_trip(&all_bytes);
    
    48
    -    }
    
    49
    -}
    11
    +pub use controller::*;
    
    12
    +pub use error::*;

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/parsers/ack.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use crate::ctor::{reply_parser::Reply, ControllerError};
    
    7
    +
    
    8
    +/// Check whether a reply contains a successful reply code and return it.
    
    9
    +/// To be used with commands we only want to check whether they were successful.
    
    10
    +/// It can be used also for notifications (and it will return their code), but
    
    11
    +/// it does not make much sense to do it.
    
    12
    +pub fn parse_ack(reply: Reply) -> Result<u16, ControllerError> {
    
    13
    +    // Ignore any detail line on purpose to allow compatbility with future
    
    14
    +    // protocol/command versions.
    
    15
    +    match ControllerError::from_reply(&reply) {
    
    16
    +        Some(e) => Err(e),
    
    17
    +        None => Ok(reply.end_line().code),
    
    18
    +    }
    
    19
    +}
    
    20
    +
    
    21
    +#[cfg(test)]
    
    22
    +mod tests {
    
    23
    +    use super::*;
    
    24
    +    use crate::ctor::reply_parser::make_reply;
    
    25
    +
    
    26
    +    #[test]
    
    27
    +    fn successful() {
    
    28
    +        assert_eq!(parse_ack(make_reply(b"250 OK\r\n")).unwrap(), 250);
    
    29
    +        assert_eq!(
    
    30
    +            parse_ack(make_reply(b"251 Also successful\r\n")).unwrap(),
    
    31
    +            251,
    
    32
    +        );
    
    33
    +        assert_eq!(
    
    34
    +            parse_ack(make_reply(b"399 Yet another one\r\n")).unwrap(),
    
    35
    +            399,
    
    36
    +        );
    
    37
    +        assert_eq!(parse_ack(make_reply(b"650 Notification\r\n")).unwrap(), 650);
    
    38
    +    }
    
    39
    +
    
    40
    +    #[test]
    
    41
    +    fn error_code() {
    
    42
    +        assert_eq!(
    
    43
    +            parse_ack(make_reply(b"400 An error\r\n")).unwrap_err(),
    
    44
    +            ControllerError::TorError {
    
    45
    +                code: 400,
    
    46
    +                message: String::from("An error"),
    
    47
    +            },
    
    48
    +        );
    
    49
    +        assert_eq!(
    
    50
    +            parse_ack(make_reply(b"599 Last error\r\n")).unwrap_err(),
    
    51
    +            ControllerError::TorError {
    
    52
    +                code: 599,
    
    53
    +                message: String::from("Last error"),
    
    54
    +            },
    
    55
    +        );
    
    56
    +    }
    
    57
    +
    
    58
    +    #[test]
    
    59
    +    fn invalid_unicode() {
    
    60
    +        assert_eq!(parse_ack(make_reply(b"250 \xFD\r\n")).unwrap(), 250);
    
    61
    +        assert_eq!(
    
    62
    +            parse_ack(make_reply(b"252-Line\r\n252 \xFD\r\n")).unwrap(),
    
    63
    +            252
    
    64
    +        );
    
    65
    +        assert_eq!(
    
    66
    +            parse_ack(make_reply(b"500 Invalid \xFD codepoint\r\n")).unwrap_err(),
    
    67
    +            ControllerError::TorError {
    
    68
    +                code: 500,
    
    69
    +                message: String::from("Invalid \u{FFFD} codepoint"),
    
    70
    +            },
    
    71
    +        );
    
    72
    +    }
    
    73
    +
    
    74
    +    #[test]
    
    75
    +    fn details_ignored() {
    
    76
    +        assert_eq!(
    
    77
    +            parse_ack(make_reply(b"250-Details\r\n250 OK\r\n")).unwrap(),
    
    78
    +            250
    
    79
    +        );
    
    80
    +        assert_eq!(
    
    81
    +            parse_ack(make_reply(
    
    82
    +                b"255-Changing status code\r\n260 Does not impact\r\n"
    
    83
    +            ))
    
    84
    +            .unwrap(),
    
    85
    +            260
    
    86
    +        );
    
    87
    +        assert_eq!(
    
    88
    +            parse_ack(make_reply(b"550-Details with error code\r\n250 OK\r\n")).unwrap(),
    
    89
    +            250
    
    90
    +        );
    
    91
    +        assert_eq!(
    
    92
    +            parse_ack(make_reply(b"250-Succesful details\r\n450 Error status\r\n")).unwrap_err(),
    
    93
    +            ControllerError::TorError {
    
    94
    +                code: 450,
    
    95
    +                message: String::from("Error status"),
    
    96
    +            },
    
    97
    +        );
    
    98
    +    }
    
    99
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.rstoolkit/components/tor-integration/tor_provider/src/ctor/controller/parsers/escape.rs

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/parsers/mod.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +mod ack;
    
    7
    +mod escape;
    
    8
    +mod unescape;
    
    9
    +
    
    10
    +#[cfg(test)]
    
    11
    +mod tests;
    
    12
    +
    
    13
    +pub use ack::parse_ack;
    
    14
    +pub use escape::tor_escape_into;
    
    15
    +pub use unescape::tor_unescape;

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/parsers/tests.rs
    1
    +// Licensed under the Apache License, Version 2.0,
    
    2
    +// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
    
    3
    +// <http://opensource.org/licenses/MIT>, at your option. This file may not be
    
    4
    +// copied, modified, or distributed except according to those terms.
    
    5
    +
    
    6
    +use super::{tor_escape_into, tor_unescape};
    
    7
    +
    
    8
    +fn round_trip(buf: &[u8]) {
    
    9
    +    let mut escaped = String::new();
    
    10
    +    tor_escape_into(buf, &mut escaped);
    
    11
    +    assert_eq!(&*tor_unescape(escaped.as_bytes()).unwrap(), buf);
    
    12
    +}
    
    13
    +
    
    14
    +#[test]
    
    15
    +fn round_trip_simple() {
    
    16
    +    round_trip(b"test");
    
    17
    +}
    
    18
    +
    
    19
    +#[test]
    
    20
    +fn round_trip_empty() {
    
    21
    +    round_trip(b"");
    
    22
    +}
    
    23
    +
    
    24
    +#[test]
    
    25
    +fn round_trip_special_chars() {
    
    26
    +    round_trip(b"'\"\\\r\n\t");
    
    27
    +}
    
    28
    +
    
    29
    +#[test]
    
    30
    +fn round_trip_non_utf8() {
    
    31
    +    round_trip(b"\xF5\xFF\xFE");
    
    32
    +}
    
    33
    +
    
    34
    +#[test]
    
    35
    +fn round_trip_all_bytes() {
    
    36
    +    // Every possible byte value, escaped then unescaped,
    
    37
    +    // must come back unchanged.
    
    38
    +    let all_bytes: Vec<u8> = (0..=255).collect();
    
    39
    +    round_trip(&all_bytes);
    
    40
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.rstoolkit/components/tor-integration/tor_provider/src/ctor/controller/parsers/unescape.rs

  • toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
    ... ... @@ -7,5 +7,6 @@ mod control_port;
    7 7
     mod controller;
    
    8 8
     mod reply_parser;
    
    9 9
     
    
    10
    -pub use control_port::{ControlPort, ControlPortError, ControlSocket, ControlSocketError};
    
    10
    +pub use control_port::{ControlPort, ControlSocket, ControlSocketError};
    
    11
    +pub use controller::*;
    
    11 12
     pub use reply_parser::{ReplyDispatcher, ReplyError};

  • toolkit/components/tor-integration/tor_provider/src/ctor/reply_parser/line.rs
    ... ... @@ -9,6 +9,7 @@ use std::{
    9 9
         ops::Deref,
    
    10 10
     };
    
    11 11
     
    
    12
    +pub const ERROR_START: u16 = 400;
    
    12 13
     pub const ASYNC_START: u16 = 600;
    
    13 14
     
    
    14 15
     /// An enum to represent MidReplyLine and DataReplyLine form the control port
    
    ... ... @@ -110,6 +111,11 @@ pub struct EndReplyLine {
    110 111
     }
    
    111 112
     
    
    112 113
     impl EndReplyLine {
    
    114
    +    /// Tells whether the line is an error.
    
    115
    +    pub fn is_error(&self) -> bool {
    
    116
    +        self.code >= ERROR_START && self.code < ASYNC_START
    
    117
    +    }
    
    118
    +
    
    113 119
         /// Tells whether the line is part of an async reply.
    
    114 120
         pub fn is_async(&self) -> bool {
    
    115 121
             self.code >= ASYNC_START
    
    ... ... @@ -146,29 +152,106 @@ mod tests {
    146 152
         }
    
    147 153
     
    
    148 154
         #[test]
    
    149
    -    fn is_async() {
    
    155
    +    fn check_type() {
    
    150 156
             {
    
    151 157
                 let r = EndReplyLine {
    
    152 158
                     code: 250,
    
    153 159
                     line: Bytes::from_static(b"OK"),
    
    154 160
                 };
    
    161
    +            assert!(!r.is_error());
    
    162
    +            assert!(!r.is_async());
    
    163
    +        }
    
    164
    +        {
    
    165
    +            let r = EndReplyLine {
    
    166
    +                code: 399,
    
    167
    +                line: Bytes::from_static(b"Success"),
    
    168
    +            };
    
    169
    +            assert!(!r.is_error());
    
    155 170
                 assert!(!r.is_async());
    
    156 171
             }
    
    172
    +        {
    
    173
    +            let r = EndReplyLine {
    
    174
    +                code: 400,
    
    175
    +                line: Bytes::from_static(b"Temporary error"),
    
    176
    +            };
    
    177
    +            assert!(r.is_error());
    
    178
    +            assert!(!r.is_async());
    
    179
    +        }
    
    180
    +        {
    
    181
    +            let r = EndReplyLine {
    
    182
    +                code: 500,
    
    183
    +                line: Bytes::from_static(b"Permanent error"),
    
    184
    +            };
    
    185
    +            assert!(r.is_error());
    
    186
    +            assert!(!r.is_async());
    
    187
    +        }
    
    188
    +        {
    
    189
    +            let r = EndReplyLine {
    
    190
    +                code: 599,
    
    191
    +                line: Bytes::from_static(b"Yet another error"),
    
    192
    +            };
    
    193
    +            assert!(r.is_error());
    
    194
    +            assert!(!r.is_async());
    
    195
    +        }
    
    196
    +        {
    
    197
    +            let r = EndReplyLine {
    
    198
    +                code: 600,
    
    199
    +                line: Bytes::from_static(b"Notification"),
    
    200
    +            };
    
    201
    +            assert!(!r.is_error());
    
    202
    +            assert!(r.is_async());
    
    203
    +        }
    
    157 204
             {
    
    158 205
                 let r = EndReplyLine {
    
    159 206
                     code: 650,
    
    160 207
                     line: Bytes::from_static(b"CIRC BUILT"),
    
    161 208
                 };
    
    209
    +            assert!(!r.is_error());
    
    162 210
                 assert!(r.is_async());
    
    163 211
             }
    
    164 212
     
    
    165 213
             {
    
    166 214
                 let r = DetailReplyLine::MidReplyLine {
    
    167 215
                     code: 250,
    
    168
    -                line: Bytes::from_static(b"key=value"),
    
    216
    +                line: Bytes::from_static(b"OK"),
    
    217
    +            };
    
    218
    +            assert!(!r.is_async());
    
    219
    +        }
    
    220
    +        {
    
    221
    +            let r = DetailReplyLine::MidReplyLine {
    
    222
    +                code: 399,
    
    223
    +                line: Bytes::from_static(b"Success"),
    
    224
    +            };
    
    225
    +            assert!(!r.is_async());
    
    226
    +        }
    
    227
    +        {
    
    228
    +            let r = DetailReplyLine::MidReplyLine {
    
    229
    +                code: 400,
    
    230
    +                line: Bytes::from_static(b"Temporary error"),
    
    231
    +            };
    
    232
    +            assert!(!r.is_async());
    
    233
    +        }
    
    234
    +        {
    
    235
    +            let r = DetailReplyLine::MidReplyLine {
    
    236
    +                code: 500,
    
    237
    +                line: Bytes::from_static(b"Permanent error"),
    
    169 238
                 };
    
    170 239
                 assert!(!r.is_async());
    
    171 240
             }
    
    241
    +        {
    
    242
    +            let r = DetailReplyLine::MidReplyLine {
    
    243
    +                code: 599,
    
    244
    +                line: Bytes::from_static(b"Yet another error"),
    
    245
    +            };
    
    246
    +            assert!(!r.is_async());
    
    247
    +        }
    
    248
    +        {
    
    249
    +            let r = DetailReplyLine::MidReplyLine {
    
    250
    +                code: 600,
    
    251
    +                line: Bytes::from_static(b"Notification"),
    
    252
    +            };
    
    253
    +            assert!(r.is_async());
    
    254
    +        }
    
    172 255
             {
    
    173 256
                 let r = DetailReplyLine::MidReplyLine {
    
    174 257
                     code: 650,
    

  • toolkit/components/tor-integration/tor_service/src/control_port.rs
    ... ... @@ -3,9 +3,9 @@
    3 3
      * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4 4
     
    
    5 5
     use nserror::nsresult;
    
    6
    -use nserror::{NS_ERROR_NOT_CONNECTED, NS_OK};
    
    6
    +use nserror::{NS_ERROR_NOT_CONNECTED, NS_ERROR_UNEXPECTED, NS_OK};
    
    7 7
     use nsstring::{nsACString, nsCString};
    
    8
    -use tor_provider::ctor::{ControlPort, ControlSocketError};
    
    8
    +use tor_provider::ctor::{ControlPort, ControlSocketError, ControllerError, TorController};
    
    9 9
     use xpcom::interfaces::{nsIFile, torITorControlPortReceiver, torITorMessageHandler};
    
    10 10
     use xpcom::RefPtr;
    
    11 11
     
    
    ... ... @@ -17,7 +17,7 @@ use super::control_socket::ControlSocketXpcom;
    17 17
     
    
    18 18
     #[xpcom(implement(torITorControlPort), atomic)]
    
    19 19
     pub struct ControlPortXpcom {
    
    20
    -    control_port: ControlPort,
    
    20
    +    control_port: TorController<ControlPort>,
    
    21 21
     }
    
    22 22
     
    
    23 23
     impl ControlPortXpcom {
    
    ... ... @@ -30,7 +30,7 @@ impl ControlPortXpcom {
    30 30
         }
    
    31 31
     
    
    32 32
         fn new(socket: Box<ControlSocketXpcom>) -> Result<RefPtr<Self>, nsresult> {
    
    33
    -        let control_port = ControlPort::new(socket).map_err(Self::map_err)?;
    
    33
    +        let control_port = TorController::new(ControlPort::new(socket).map_err(Self::map_err)?);
    
    34 34
             Ok(Self::allocate(InitControlPortXpcom { control_port }))
    
    35 35
         }
    
    36 36
     
    
    ... ... @@ -78,7 +78,7 @@ impl ControlPortXpcom {
    78 78
                 command.extend_from_slice(b"\r\n");
    
    79 79
             }
    
    80 80
             let handler = RefPtr::new(handler);
    
    81
    -        self.control_port.send_command(
    
    81
    +        self.control_port.send_raw_command(
    
    82 82
                 command.into(),
    
    83 83
                 Box::new(move |reply| {
    
    84 84
                     let mut buf = Vec::new();
    
    ... ... @@ -110,7 +110,10 @@ impl ControlPortXpcom {
    110 110
     
    
    111 111
         xpcom_method!(close => Close());
    
    112 112
         pub fn close(&self) -> Result<(), nsresult> {
    
    113
    -        self.control_port.close().map_err(Self::map_err)
    
    113
    +        self.control_port.close().map_err(|e| match e {
    
    114
    +            ControllerError::ConnectionError(rv) => nsresult(rv),
    
    115
    +            _ => NS_ERROR_UNEXPECTED,
    
    116
    +        })
    
    114 117
         }
    
    115 118
     
    
    116 119
         fn map_err(e: ControlSocketError) -> nsresult {
    

  • _______________________________________________
    tor-commits mailing list -- tor-commits@xxxxxxxxxxxxxxxxxxxx
    To unsubscribe send an email to tor-commits-leave@xxxxxxxxxxxxxxxxxxxx