[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] 2 commits: fixup! TB 44806: Implement the tor integration in Rust.



Title: GitLab

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

Commits:

  • 0e74ee9b
    by Elena at 2026-08-31T17:50:01+02:00
    fixup! TB 44806: Implement the tor integration in Rust.
    
    TB 44930: Implement the commands on the Rust control port
    
    Implemented string unescape.
    
  • 12016c7b
    by Elena at 2026-08-31T17:50:01+02:00
    fixup! TB 44806: Implement the tor integration in Rust.
    
    TB 44930: Implement the commands on the Rust control port
    
    Implemented the string escaping.
    

4 changed files:

Changes:

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/escape.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 std::fmt::Write;
    
    7
    +
    
    8
    +/// Adaptation of tor's esc_for_log (src/lib/log/escape.c).
    
    9
    +///
    
    10
    +/// However, we prefer the hex representation to the octal one.
    
    11
    +pub fn tor_escape_into<T: AsRef<[u8]>>(buf: T, dest: &mut String) {
    
    12
    +    dest.reserve(buf.as_ref().len() + 2);
    
    13
    +    dest.push('"');
    
    14
    +    for b in buf.as_ref() {
    
    15
    +        match *b {
    
    16
    +            b'\'' | b'"' | b'\\' => {
    
    17
    +                dest.push('\\');
    
    18
    +                dest.push(*b as char);
    
    19
    +            }
    
    20
    +            b'\n' => {
    
    21
    +                dest.push_str("\\n");
    
    22
    +            }
    
    23
    +            b'\t' => {
    
    24
    +                dest.push_str("\\t");
    
    25
    +            }
    
    26
    +            b'\r' => {
    
    27
    +                dest.push_str("\\r");
    
    28
    +            }
    
    29
    +            0x20..=0x7E => {
    
    30
    +                dest.push(*b as char);
    
    31
    +            }
    
    32
    +            _ => {
    
    33
    +                write!(dest, "\\x{:02X}", b).expect("String::write_str always returns Ok(()).");
    
    34
    +            }
    
    35
    +        }
    
    36
    +    }
    
    37
    +    dest.push('"');
    
    38
    +}
    
    39
    +
    
    40
    +#[cfg(test)]
    
    41
    +mod tests {
    
    42
    +    use super::*;
    
    43
    +
    
    44
    +    fn tor_escape<T: AsRef<[u8]>>(buf: T) -> String {
    
    45
    +        let mut dest = String::new();
    
    46
    +        tor_escape_into(buf, &mut dest);
    
    47
    +        dest
    
    48
    +    }
    
    49
    +
    
    50
    +    #[test]
    
    51
    +    fn simple() {
    
    52
    +        assert_eq!(tor_escape("test"), "\"test\"");
    
    53
    +    }
    
    54
    +
    
    55
    +    #[test]
    
    56
    +    fn empty() {
    
    57
    +        assert_eq!(tor_escape(""), "\"\"");
    
    58
    +    }
    
    59
    +
    
    60
    +    #[test]
    
    61
    +    fn escaped() {
    
    62
    +        assert_eq!(tor_escape("'\"\\\r\n\t"), "\"\\'\\\"\\\\\\r\\n\\t\"");
    
    63
    +        assert_eq!(tor_escape("\0"), "\"\\x00\"");
    
    64
    +        assert_eq!(tor_escape("\u{1F9C5}"), "\"\\xF0\\x9F\\xA7\\x85\"");
    
    65
    +    }
    
    66
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/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 escape;
    
    7
    +mod unescape;
    
    8
    +
    
    9
    +use escape::*;
    
    10
    +use unescape::*;

  • toolkit/components/tor-integration/tor_provider/src/ctor/controller/unescape.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 std::borrow::Cow;
    
    7
    +use thiserror::Error;
    
    8
    +
    
    9
    +#[derive(Error, Debug, PartialEq, Eq)]
    
    10
    +pub enum UnescapeError {
    
    11
    +    #[error("space in an unquoted string")]
    
    12
    +    SpaceInUnquotedString,
    
    13
    +    #[error("unterminated escape sequence or string")]
    
    14
    +    Unterminated,
    
    15
    +    #[error("found an unescaped quote")]
    
    16
    +    UnescapedQuote,
    
    17
    +    #[error("found an invalid escape sequence")]
    
    18
    +    InvalidEscape,
    
    19
    +    #[error("an octal-escaped value exceeds 255")]
    
    20
    +    OctalOverflow,
    
    21
    +}
    
    22
    +
    
    23
    +/// Adaptation of tor's unescape_string (src/lib/encoding/cstring.c).
    
    24
    +/// The main difference is that we allow unquoted strings.
    
    25
    +pub fn tor_unescape<'a>(buf: &'a [u8]) -> Result<Cow<'a, [u8]>, UnescapeError> {
    
    26
    +    match buf.first() {
    
    27
    +        Some(b'"') => {
    
    28
    +            // Quoted string, consume the item and continue with the function.
    
    29
    +        }
    
    30
    +        _ => {
    
    31
    +            if buf.contains(&b' ') {
    
    32
    +                return Err(UnescapeError::SpaceInUnquotedString);
    
    33
    +            }
    
    34
    +            return Ok(Cow::Borrowed(buf));
    
    35
    +        }
    
    36
    +    }
    
    37
    +
    
    38
    +    if buf.len() == 1 || buf.last() != Some(&b'\"') {
    
    39
    +        return Err(UnescapeError::Unterminated);
    
    40
    +    }
    
    41
    +
    
    42
    +    // This is used only if we did an actual change.
    
    43
    +    let mut out = None;
    
    44
    +
    
    45
    +    // We already consumed the initial quote.
    
    46
    +    let mut i = 1;
    
    47
    +    while i < buf.len() {
    
    48
    +        let c = buf[i];
    
    49
    +        i += 1;
    
    50
    +        match c {
    
    51
    +            b'\"' => {
    
    52
    +                if i != buf.len() {
    
    53
    +                    return Err(UnescapeError::UnescapedQuote);
    
    54
    +                }
    
    55
    +                return Ok(out
    
    56
    +                    .map(|b| Cow::Owned(b))
    
    57
    +                    .unwrap_or_else(|| Cow::Borrowed(&buf[1..buf.len() - 1])));
    
    58
    +            }
    
    59
    +            0 | b'\n' => return Err(UnescapeError::Unterminated),
    
    60
    +            b'\\' => {
    
    61
    +                let esc = buf.get(i).ok_or(UnescapeError::Unterminated)?;
    
    62
    +                i += 1;
    
    63
    +                let out = out.get_or_insert_with(|| {
    
    64
    +                    let mut b = Vec::with_capacity(buf.len() - 2);
    
    65
    +                    b.extend(&buf[1..i - 2]);
    
    66
    +                    b
    
    67
    +                });
    
    68
    +                out.push(match esc {
    
    69
    +                    b'n' => Ok(b'\n'),
    
    70
    +                    b'r' => Ok(b'\r'),
    
    71
    +                    b't' => Ok(b'\t'),
    
    72
    +                    b'x' | b'X' => unescape_hex(buf, &mut i),
    
    73
    +                    b'0'..=b'7' => unescape_octal(buf, &mut i),
    
    74
    +                    b'\'' | b'"' | b'\\' => Ok(*esc),
    
    75
    +                    _ => Err(UnescapeError::InvalidEscape),
    
    76
    +                }?);
    
    77
    +            }
    
    78
    +            _ => {
    
    79
    +                if let Some(b) = out.as_mut() {
    
    80
    +                    b.push(c);
    
    81
    +                }
    
    82
    +            }
    
    83
    +        }
    
    84
    +    }
    
    85
    +
    
    86
    +    // If we fall out of the loop we never saw a closing quote.
    
    87
    +    Err(UnescapeError::Unterminated)
    
    88
    +}
    
    89
    +
    
    90
    +fn unescape_hex(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
    
    91
    +    // The C code expects exactly two hex digits.
    
    92
    +    *i += 2;
    
    93
    +    str::from_utf8(&buf[*i - 2..*i])
    
    94
    +        .ok()
    
    95
    +        .and_then(|s| u8::from_str_radix(s, 16).ok())
    
    96
    +        .ok_or(UnescapeError::InvalidEscape)
    
    97
    +}
    
    98
    +
    
    99
    +fn unescape_octal(buf: &[u8], i: &mut usize) -> Result<u8, UnescapeError> {
    
    100
    +    *i -= 1;
    
    101
    +    let mut len = 1;
    
    102
    +    while (*i + len) < buf.len() && len < 3 {
    
    103
    +        let b = buf[*i + len];
    
    104
    +        if b >= b'0' && b <= b'7' {
    
    105
    +            len += 1;
    
    106
    +        } else {
    
    107
    +            break;
    
    108
    +        }
    
    109
    +    }
    
    110
    +
    
    111
    +    *i += len;
    
    112
    +    // This should never fail with InvalidEscape, as we have just validated the
    
    113
    +    // octal values.
    
    114
    +    str::from_utf8(&buf[*i - len..*i])
    
    115
    +        .ok()
    
    116
    +        .and_then(|s| u16::from_str_radix(s, 8).ok())
    
    117
    +        .ok_or(UnescapeError::InvalidEscape)
    
    118
    +        .and_then(|v| v.try_into().map_err(|_| UnescapeError::OctalOverflow))
    
    119
    +}
    
    120
    +
    
    121
    +#[cfg(test)]
    
    122
    +mod tests {
    
    123
    +    use super::*;
    
    124
    +
    
    125
    +    #[test]
    
    126
    +    fn simple() {
    
    127
    +        assert_eq!(&*tor_unescape(b"test").unwrap(), b"test");
    
    128
    +        assert_eq!(&*tor_unescape(b"\"test\"").unwrap(), b"test");
    
    129
    +    }
    
    130
    +
    
    131
    +    #[test]
    
    132
    +    fn quote_in_unquoted() {
    
    133
    +        assert_eq!(&*tor_unescape(b"te\"st").unwrap(), b"te\"st");
    
    134
    +        assert_eq!(&*tor_unescape(b"test\"").unwrap(), b"test\"");
    
    135
    +    }
    
    136
    +
    
    137
    +    #[test]
    
    138
    +    fn empty() {
    
    139
    +        assert_eq!(&*tor_unescape(b"").unwrap(), &[]);
    
    140
    +        assert_eq!(&*tor_unescape(b"\"\"").unwrap(), &[]);
    
    141
    +    }
    
    142
    +
    
    143
    +    #[test]
    
    144
    +    fn unescape_simple() {
    
    145
    +        assert_eq!(&*tor_unescape(b"\"\\n\"").unwrap(), b"\n");
    
    146
    +        assert_eq!(&*tor_unescape(b"\"\\r\"").unwrap(), b"\r");
    
    147
    +        assert_eq!(&*tor_unescape(b"\"\\t\"").unwrap(), b"\t");
    
    148
    +        assert_eq!(&*tor_unescape(b"\"\\r\\n\"").unwrap(), b"\r\n");
    
    149
    +        assert_eq!(&*tor_unescape(b"\"'\"").unwrap(), b"'");
    
    150
    +        assert_eq!(&*tor_unescape(b"\"\\'\"").unwrap(), b"'");
    
    151
    +        assert_eq!(&*tor_unescape(b"\"\\\"\"").unwrap(), b"\"");
    
    152
    +        assert_eq!(&*tor_unescape(b"\"\\\\\"").unwrap(), b"\\");
    
    153
    +    }
    
    154
    +
    
    155
    +    #[test]
    
    156
    +    fn unescape_hex() {
    
    157
    +        assert_eq!(&*tor_unescape(b"\"\\x20\"").unwrap(), b" ");
    
    158
    +        assert_eq!(&*tor_unescape(b"\"\\x20test\"").unwrap(), b" test");
    
    159
    +        assert_eq!(&*tor_unescape(b"\"test\\x20\"").unwrap(), b"test ");
    
    160
    +        assert_eq!(&*tor_unescape(b"\"te\\x20st\"").unwrap(), b"te st");
    
    161
    +
    
    162
    +        assert_eq!(&*tor_unescape(b"\"\\X20\"").unwrap(), b" ");
    
    163
    +        assert_eq!(&*tor_unescape(b"\"\\X20test\"").unwrap(), b" test");
    
    164
    +        assert_eq!(&*tor_unescape(b"\"test\\X20\"").unwrap(), b"test ");
    
    165
    +        assert_eq!(&*tor_unescape(b"\"te\\X20st\"").unwrap(), b"te st");
    
    166
    +
    
    167
    +        assert_eq!(&*tor_unescape(b"\"\\x00\"").unwrap(), b"\0");
    
    168
    +        assert_eq!(&*tor_unescape(b"\"test\\x00\"").unwrap(), b"test\0");
    
    169
    +        assert_eq!(&*tor_unescape(b"\"\\x00test\"").unwrap(), b"\0test");
    
    170
    +        assert_eq!(&*tor_unescape(b"\"\\X00\"").unwrap(), b"\0");
    
    171
    +        assert_eq!(&*tor_unescape(b"\"TEST\\X00\"").unwrap(), b"TEST\0");
    
    172
    +        assert_eq!(&*tor_unescape(b"\"\\X00TEST\"").unwrap(), b"\0TEST");
    
    173
    +    }
    
    174
    +
    
    175
    +    #[test]
    
    176
    +    fn unescape_octal() {
    
    177
    +        assert_eq!(&*tor_unescape(b"\"\\0\"").unwrap(), b"\0");
    
    178
    +        assert_eq!(&*tor_unescape(b"\"\\00\"").unwrap(), b"\0");
    
    179
    +        assert_eq!(&*tor_unescape(b"\"\\000\"").unwrap(), b"\0");
    
    180
    +
    
    181
    +        assert_eq!(&*tor_unescape(b"\"\\2\"").unwrap(), b"\x02");
    
    182
    +        assert_eq!(&*tor_unescape(b"\"\\02\"").unwrap(), b"\x02");
    
    183
    +        assert_eq!(&*tor_unescape(b"\"\\002\"").unwrap(), b"\x02");
    
    184
    +
    
    185
    +        assert_eq!(&*tor_unescape(b"\"\\40\"").unwrap(), b" ");
    
    186
    +        assert_eq!(&*tor_unescape(b"\"\\040\"").unwrap(), b" ");
    
    187
    +
    
    188
    +        assert_eq!(&*tor_unescape(b"\"\\40test\"").unwrap(), b" test");
    
    189
    +        assert_eq!(&*tor_unescape(b"\"\\040test\"").unwrap(), b" test");
    
    190
    +        assert_eq!(&*tor_unescape(b"\"\\40test\\0\"").unwrap(), b" test\0");
    
    191
    +    }
    
    192
    +
    
    193
    +    #[test]
    
    194
    +    fn invalid_unicode() {
    
    195
    +        // Raw invalid sequence, without quotes
    
    196
    +        assert_eq!(*tor_unescape(b"\xF5").unwrap(), [0xF5u8]);
    
    197
    +        // Raw invalid sequence, with quotes
    
    198
    +        assert_eq!(*tor_unescape(b"\"\xF5\"").unwrap(), [0xF5u8]);
    
    199
    +        // Escaped invalid values, we will unescape but keep them as they are.
    
    200
    +        assert_eq!(*tor_unescape(b"\"\\xF5\"").unwrap(), [0xF5u8]);
    
    201
    +        assert_eq!(*tor_unescape(b"\"\\365\"").unwrap(), [0xF5u8]);
    
    202
    +    }
    
    203
    +
    
    204
    +    #[test]
    
    205
    +    fn unquoted_space() {
    
    206
    +        assert_eq!(
    
    207
    +            tor_unescape(b"test test").unwrap_err(),
    
    208
    +            UnescapeError::SpaceInUnquotedString
    
    209
    +        );
    
    210
    +    }
    
    211
    +
    
    212
    +    #[test]
    
    213
    +    fn unterminated() {
    
    214
    +        assert_eq!(
    
    215
    +            tor_unescape(b"\"test").unwrap_err(),
    
    216
    +            UnescapeError::Unterminated
    
    217
    +        );
    
    218
    +        assert_eq!(
    
    219
    +            tor_unescape(b"\"test\\\"").unwrap_err(),
    
    220
    +            UnescapeError::Unterminated
    
    221
    +        );
    
    222
    +        assert_eq!(
    
    223
    +            tor_unescape(b"\"test\n\"").unwrap_err(),
    
    224
    +            UnescapeError::Unterminated
    
    225
    +        );
    
    226
    +        assert_eq!(
    
    227
    +            tor_unescape(b"\"test\n").unwrap_err(),
    
    228
    +            UnescapeError::Unterminated
    
    229
    +        );
    
    230
    +        assert_eq!(
    
    231
    +            tor_unescape(b"\"test\0\"").unwrap_err(),
    
    232
    +            UnescapeError::Unterminated
    
    233
    +        );
    
    234
    +        assert_eq!(
    
    235
    +            tor_unescape(b"\"test\0").unwrap_err(),
    
    236
    +            UnescapeError::Unterminated
    
    237
    +        );
    
    238
    +
    
    239
    +        // Not having a final quote shortcircuits other errors.
    
    240
    +        assert_eq!(
    
    241
    +            tor_unescape(b"\"test \" test").unwrap_err(),
    
    242
    +            UnescapeError::Unterminated
    
    243
    +        );
    
    244
    +    }
    
    245
    +
    
    246
    +    #[test]
    
    247
    +    fn unescaped_quote() {
    
    248
    +        assert_eq!(
    
    249
    +            tor_unescape(b"\"test \" test\"").unwrap_err(),
    
    250
    +            UnescapeError::UnescapedQuote
    
    251
    +        );
    
    252
    +    }
    
    253
    +
    
    254
    +    #[test]
    
    255
    +    fn invalid_escape() {
    
    256
    +        assert_eq!(
    
    257
    +            tor_unescape(b"\"\\z\"").unwrap_err(),
    
    258
    +            UnescapeError::InvalidEscape,
    
    259
    +        );
    
    260
    +
    
    261
    +        assert_eq!(
    
    262
    +            tor_unescape(b"\"\\xy\"").unwrap_err(),
    
    263
    +            UnescapeError::InvalidEscape,
    
    264
    +        );
    
    265
    +        assert_eq!(
    
    266
    +            tor_unescape(b"\"\\XY\"").unwrap_err(),
    
    267
    +            UnescapeError::InvalidEscape,
    
    268
    +        );
    
    269
    +        assert_eq!(
    
    270
    +            tor_unescape(b"\"\\x1Y\"").unwrap_err(),
    
    271
    +            UnescapeError::InvalidEscape,
    
    272
    +        );
    
    273
    +        assert_eq!(
    
    274
    +            tor_unescape(b"\"\\x1\"").unwrap_err(),
    
    275
    +            UnescapeError::InvalidEscape,
    
    276
    +        );
    
    277
    +        assert_eq!(
    
    278
    +            tor_unescape(b"\"\\x\\xF5z\"").unwrap_err(),
    
    279
    +            UnescapeError::InvalidEscape,
    
    280
    +        );
    
    281
    +    }
    
    282
    +
    
    283
    +    #[test]
    
    284
    +    fn octal_overflow() {
    
    285
    +        assert_eq!(
    
    286
    +            tor_unescape(b"\"\\777\"").unwrap_err(),
    
    287
    +            UnescapeError::OctalOverflow,
    
    288
    +        );
    
    289
    +    }
    
    290
    +}

  • toolkit/components/tor-integration/tor_provider/src/ctor/mod.rs
    ... ... @@ -4,6 +4,7 @@
    4 4
     // copied, modified, or distributed except according to those terms.
    
    5 5
     
    
    6 6
     mod control_port;
    
    7
    +mod controller;
    
    7 8
     mod reply_parser;
    
    8 9
     
    
    9 10
     pub use control_port::{ControlPort, ControlPortError, ControlSocket, ControlSocketError};
    

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