blob: 54967f7dc121b57d733bc606425fe6e4ada623c2 [file] [edit]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
util::{into_raw_descriptor, ProxyConfig},
Connector, DnsManager, Result,
};
use bytes::Bytes;
use libslirp_rs::libslirp::{ProxyConnect, ProxyManager};
use log::{debug, warn};
use std::net::SocketAddr;
use std::sync::{mpsc, Arc};
use std::thread;
use tokio::runtime::Runtime;
/// # Manager
///
/// The `Manager` struct implements the `ProxyManager` trait from
/// `libslirp_rs`. It is responsible for managing TCP connections
/// through an HTTP proxy using the `Connector` struct.
///
/// The `Manager` uses a `tokio::runtime::Runtime` to spawn tasks for
/// establishing proxy connections. It takes a proxy configuration
/// string as input, which is parsed into a `ProxyConfig` to create a
/// `Connector` instance.
///
/// The `try_connect` method attempts to establish a connection to the
/// given `SocketAddr` through the proxy. If successful, it calls the
/// `proxy_connect` function with the raw file descriptor of the
/// connected socket.
///
/// # Example
///
/// ```
/// use std::net::SocketAddr;
/// use libslirp_rs::libslirp::ProxyConnect;
///
/// struct MyProxyConnect;
///
/// impl ProxyConnect for MyProxyConnect {
/// fn proxy_connect(&self, fd: i32, sockaddr: SocketAddr) {
/// // Handle the connected socket
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// }
/// ```
pub struct Manager {
runtime: Arc<Runtime>,
connector: Connector,
dns_manager: Arc<DnsManager>,
}
impl Manager {
/// Creates a new `LibSlirp` instance.
///
/// This function initializes the libslirp library and spawns the necessary threads
/// for handling network traffic and polling.
pub fn new(proxy: &str, rx_proxy_bytes: mpsc::Receiver<Bytes>) -> Result<Self> {
let config = ProxyConfig::from_string(proxy)?;
let dns_manager = Arc::new(DnsManager::new());
let dns_manager_clone = dns_manager.clone();
let _ = thread::Builder::new().name("Dns Manager".to_string()).spawn(move || {
while let Ok(bytes) = rx_proxy_bytes.recv() {
dns_manager_clone.add_from_ethernet_slice(&bytes);
}
});
Ok(Self {
runtime: Arc::new(Runtime::new()?),
connector: Connector::new(config.addr, config.username, config.password),
dns_manager,
})
}
}
impl ProxyManager for Manager {
/// Attempts to establish a TCP connection to the given `sockaddr` through the proxy.
///
/// This function spawns a new task in the `tokio` runtime to handle the connection process.
/// If the connection is successful, it calls the `proxy_connect` function of the provided
/// `ProxyConnect` object with the raw file descriptor of the connected socket.
///
/// # Arguments
///
/// * `sockaddr` - The target socket address to connect to.
/// * `connect_id` - An identifier for the connection.
/// * `connect_func` - A `ProxyConnect` object that will be called with the connected socket.
///
/// # Returns
///
/// `true` if the connection attempt was initiated, `false` otherwise.
fn try_connect(
&self,
sockaddr: SocketAddr,
connect_id: usize,
connect_func: Box<dyn ProxyConnect + Send>,
) -> bool {
debug!("Connecting to {sockaddr:?} with connect ID {connect_id}");
let connector = self.connector.clone();
self.runtime.handle().spawn(async move {
let fd = match connector.connect(sockaddr).await {
Ok(tcp_stream) => into_raw_descriptor(tcp_stream),
Err(e) => {
warn!("Failed to connect to proxy {}. {}", sockaddr, e);
-1
}
};
connect_func.proxy_connect(fd, sockaddr);
});
true
}
/// Removes a connection with the given `connect_id`.
///
/// Currently, this function only logs a debug message.
///
/// # Arguments
///
/// * `connect_id` - The identifier of the connection to remove.
fn remove(&self, connect_id: usize) {
debug!("Remove connect ID {}", connect_id);
}
}