Commit 32c91487 authored by Elena's avatar Elena 🤷‍♀️ Committed by brizental
Browse files

fixup! TB 44806: Implement the tor integration in Rust.

TB 44924: Create a control port client in Rust.

Implemented a basic control port.
parent 22bb67db
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8123,6 +8123,7 @@ name = "tor_provider"
version = "0.1.0"
dependencies = [
 "bytes",
 "log",
 "memchr",
 "thiserror 2.0.12",
]
+1 −0
Original line number Diff line number Diff line
@@ -6,5 +6,6 @@ edition = "2021"

[dependencies]
bytes = "1.4.0"
log = "0.4"
memchr = "2.7.4"
thiserror = "2"
+95 −0
Original line number Diff line number Diff line
// Licensed under the Apache License, Version 2.0,
// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
// <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use bytes::{BufMut, Bytes, BytesMut};
use std::{cell::Cell, rc::Rc};

use super::{control_socket::*, error::ControlPortError};
use crate::ctor::reply_parser::{Reply, ReplyDispatcher, ReplyError};

/// The lower-level part of the control port implementation.
/// It contains the logic for actually sending the command, and it hides its
/// reference-counted nature from actual consumers.
struct ControlPortInner {
    reply_dispatcher: ReplyDispatcher,
    socket: Rc<dyn ControlSocket>,
    closed: Cell<bool>,
}

impl ControlPortInner {
    fn new(socket: Rc<dyn ControlSocket>) -> Result<Rc<Self>, ControlSocketError> {
        Ok(Rc::new(Self {
            reply_dispatcher: ReplyDispatcher::new(),
            socket,
            closed: Cell::new(false),
        }))
        // TODO: Start the message pump.
    }

    fn close(&self) -> Result<(), ControlSocketError> {
        if self.closed.replace(true) {
            return Ok(());
        }
        self.reply_dispatcher.fail_all(ReplyError::ConnectionClosed);
        self.socket.close()
    }

    fn send_command(
        &self,
        mut command: Bytes,
        handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>,
    ) {
        if self.closed.get() {
            handler(Err(ControlPortError::ConnectionError(
                ControlSocketError::ConnectionClosed,
            )));
            return;
        }

        if !command.ends_with(b"\r\n") {
            let mut buf = BytesMut::from(command);
            buf.put(&b"\r\n"[..]);
            command = buf.freeze();
        }

        // TODO: Implement.
    }
}

impl Drop for ControlPortInner {
    fn drop(&mut self) {
        if let Err(e) = self.close() {
            log::error!(
                "Failed to close the control socket from drop: {}",
                e.to_string()
            );
        }
    }
}

pub struct ControlPort(Rc<ControlPortInner>);

impl ControlPort {
    #[inline]
    pub fn new(socket: Box<dyn ControlSocket>) -> Result<Self, ControlSocketError> {
        Ok(Self(ControlPortInner::new(Rc::from(socket))?))
    }

    #[inline]
    pub fn close(&self) -> Result<(), ControlSocketError> {
        self.0.close()
    }

    // TODO: Keep only the methods speicifc to commands and remove this one
    // (tor-browser#44930).
    #[inline]
    pub fn send_command(
        &self,
        command: Bytes,
        handler: Box<dyn FnOnce(Result<Reply, ControlPortError>)>,
    ) {
        self.0.send_command(command, handler);
    }
}
+59 −0
Original line number Diff line number Diff line
// Licensed under the Apache License, Version 2.0,
// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
// <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use bytes::Bytes;
use thiserror::Error;

#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum ControlSocketError {
    /// A wrapper for XPCOM error codes (nsresult), that are 32-bit unsigned
    /// integers. We cannot use nsresult directly as this crate does not link to
    /// XPCOM.
    #[error("implementation error: {0:08X}")]
    ImplementationError(u32),
    #[error("the connection has been closed")]
    ConnectionClosed,
}

/// The I/O layer for the control port.
/// The implementation should manage thread safety, to make sure everything
/// happens in the thread of the caller.
pub trait ControlSocket {
    /// Queue a read.
    fn queue_read(&self, f: Box<dyn FnOnce()>) -> Result<(), ControlSocketError>;

    /// Tell how many bytes can be read from the socket.
    /// Returns 0 if the peer closed the connection.
    fn available(&self) -> Result<u32, ControlSocketError>;

    /// Read at most max_len from the socket.
    /// The caller is expected to support partial reads.
    /// However, an empty result will be treated as an EOF/connection closed.
    /// Also, the caller is expected to call read only after a calback
    /// registered with queue_read was called.
    fn read(&self, max_len: u32) -> Result<Bytes, ControlSocketError>;

    /// Queue a write.
    /// For better ergonomics, implementations should report also synchronous
    /// errors through the callback, as callers will have to implement an async
    /// error strategy anyway.
    fn queue_write(&self, f: Box<dyn FnOnce(Result<(), ControlSocketError>)>, len: u32);

    /// Write to the socket.
    /// The caller is expected to handle partial writes (after calling
    /// queue_write again).
    /// Also, the caller is expected to call write only after a callback
    /// registered with queue_write was called.
    /// The caller is expected not to pass an empty buffer. Implementations are
    /// allowed to return errors in that case.
    /// Notice that 0 bytes written is an allowed value, but the caller might
    /// queue another write immediately. The socket implementation should call
    /// the new callback only when there is actually some room for writing, to
    /// avoid potential infinite loops.
    fn write(&self, buffer: &Bytes) -> Result<u32, ControlSocketError>;

    /// Close the socket.
    fn close(&self) -> Result<(), ControlSocketError>;
}
+18 −0
Original line number Diff line number Diff line
// Licensed under the Apache License, Version 2.0,
// <http://apache.org/licenses/LICENSE-2.0> or the MIT license
// <http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use thiserror::Error;

use super::super::{ControlSocketError, ReplyError};

#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum ControlPortError {
    #[error("connection error")]
    ConnectionError(#[from] ControlSocketError),
    #[error("protocol violation")]
    ProtocolError(#[from] ReplyError),
    #[error("unsuccessful command ({code}): {message}")]
    TorError { code: u16, message: String },
}
Loading