1 // Copyright 2024 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use crate::util::{into_raw_descriptor, ProxyConfig}; 16 use crate::{connector::Connector, error::Error}; 17 use libslirp_rs::libslirp::{ProxyConnect, ProxyManager}; 18 use log::{debug, warn}; 19 use std::net::SocketAddr; 20 use std::sync::Arc; 21 use tokio::runtime::Runtime; 22 23 /// # Manager 24 /// 25 /// The `Manager` struct implements the `ProxyManager` trait from 26 /// `libslirp_rs`. It is responsible for managing TCP connections 27 /// through an HTTP proxy using the `Connector` struct. 28 /// 29 /// The `Manager` uses a `tokio::runtime::Runtime` to spawn tasks for 30 /// establishing proxy connections. It takes a proxy configuration 31 /// string as input, which is parsed into a `ProxyConfig` to create a 32 /// `Connector` instance. 33 /// 34 /// The `try_connect` method attempts to establish a connection to the 35 /// given `SocketAddr` through the proxy. If successful, it calls the 36 /// `proxy_connect` function with the raw file descriptor of the 37 /// connected socket. 38 /// 39 /// # Example 40 /// 41 /// ``` 42 /// use std::net::SocketAddr; 43 /// use libslirp_rs::libslirp::ProxyConnect; 44 /// 45 /// struct MyProxyConnect; 46 /// 47 /// impl ProxyConnect for MyProxyConnect { 48 /// fn proxy_connect(&self, fd: i32, sockaddr: SocketAddr) { 49 /// // Handle the connected socket 50 /// } 51 /// } 52 /// 53 /// #[tokio::main] 54 /// async fn main() { 55 /// } 56 /// ``` 57 pub struct Manager { 58 runtime: Arc<Runtime>, 59 connector: Connector, 60 } 61 62 impl Manager { new(proxy: &str) -> Result<Self, Error>63 pub fn new(proxy: &str) -> Result<Self, Error> { 64 let config = ProxyConfig::from_string(&proxy)?; 65 Ok(Self { 66 runtime: Arc::new(Runtime::new()?), 67 connector: Connector::new(config.addr, config.username, config.password), 68 }) 69 } 70 } 71 72 impl ProxyManager for Manager { 73 /// Attempts to establish a TCP connection to the given `sockaddr` through the proxy. 74 /// 75 /// This function spawns a new task in the `tokio` runtime to handle the connection process. 76 /// If the connection is successful, it calls the `proxy_connect` function of the provided 77 /// `ProxyConnect` object with the raw file descriptor of the connected socket. 78 /// 79 /// # Arguments 80 /// 81 /// * `sockaddr` - The target socket address to connect to. 82 /// * `connect_id` - An identifier for the connection. 83 /// * `connect_func` - A `ProxyConnect` object that will be called with the connected socket. 84 /// 85 /// # Returns 86 /// 87 /// `true` if the connection attempt was initiated, `false` otherwise. try_connect( &self, sockaddr: SocketAddr, connect_id: usize, connect_func: Box<dyn ProxyConnect + Send>, ) -> bool88 fn try_connect( 89 &self, 90 sockaddr: SocketAddr, 91 connect_id: usize, 92 connect_func: Box<dyn ProxyConnect + Send>, 93 ) -> bool { 94 debug!("Connecting to {sockaddr:?} with connect ID {connect_id}"); 95 let connector = self.connector.clone(); 96 97 self.runtime.handle().spawn(async move { 98 let fd = match connector.connect(sockaddr).await { 99 Ok(tcp_stream) => into_raw_descriptor(tcp_stream), 100 Err(e) => { 101 warn!("Failed to connect to proxy {}. {}", sockaddr, e); 102 -1 103 } 104 }; 105 connect_func.proxy_connect(fd, sockaddr); 106 }); 107 108 true 109 } 110 111 /// Removes a connection with the given `connect_id`. 112 /// 113 /// Currently, this function only logs a debug message. 114 /// 115 /// # Arguments 116 /// 117 /// * `connect_id` - The identifier of the connection to remove. remove(&self, connect_id: usize)118 fn remove(&self, connect_id: usize) { 119 debug!("Remove connect ID {}", connect_id); 120 } 121 } 122