diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..3803243 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,50 @@ +#[derive(Debug)] +pub struct Error { + message: String, +} + +pub fn s2e(s: &str) -> Error { + Error::from(s) +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + Self { + message: err.to_string(), + } + } +} + +impl From<&str> for Error { + fn from(err: &str) -> Self { + Self { + message: err.to_string(), + } + } +} + +impl From for Error { + fn from(err: String) -> Self { + Self { message: err } + } +} + +impl From<&String> for Error { + fn from(err: &String) -> Self { + Self { + message: err.to_string(), + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + &self.message + } +} diff --git a/src/http.rs b/src/http.rs index 31885b6..c3aec4d 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,6 +1,7 @@ +use crate::error::Error; use crate::tun2proxy::{ Connection, ConnectionManager, Credentials, IncomingDataEvent, IncomingDirection, - OutgoingDataEvent, OutgoingDirection, ProxyError, TcpProxy, + OutgoingDataEvent, OutgoingDirection, TcpProxy, }; use base64::Engine; use std::collections::VecDeque; @@ -58,7 +59,7 @@ impl HttpConnection { } } - fn state_change(&mut self) -> Result<(), ProxyError> { + fn state_change(&mut self) -> Result<(), Error> { match self.state { HttpState::ExpectStatusCode if self.server_inbuf.len() >= "HTTP/1.1 200 ".len() => { let status_line: Vec = self @@ -72,11 +73,9 @@ impl HttpConnection { { let status_str = String::from_utf8_lossy(&status_line.as_slice()[0.."HTTP/1.1 200".len()]); - return Err(ProxyError::new( - "Expected success status code. Server replied with ".to_owned() - + &*status_str - + ".", - )); + let e = + format!("Expected success status code. Server replied with {status_str}."); + return Err(e.into()); } self.state = HttpState::ExpectResponse; return self.state_change(); @@ -118,7 +117,7 @@ impl HttpConnection { } impl TcpProxy for HttpConnection { - fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), ProxyError> { + fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> { let direction = event.direction; let buffer = event.buffer; match direction { diff --git a/src/lib.rs b/src/lib.rs index 40ce19c..248e8b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ +use crate::error::{s2e, Error}; use crate::tun2proxy::Credentials; use crate::{http::HttpManager, socks5::Socks5Manager, tun2proxy::TunToProxy}; use std::net::{SocketAddr, ToSocketAddrs}; +pub mod error; pub mod http; pub mod socks5; pub mod tun2proxy; @@ -15,24 +17,23 @@ pub struct Proxy { } impl Proxy { - pub fn from_url(s: &str) -> Result { - let url = url::Url::parse(s).map_err(|_| format!("`{s}` is not a valid proxy URL"))?; - let host = url - .host_str() - .ok_or(format!("`{s}` does not contain a host"))?; + pub fn from_url(s: &str) -> Result { + let e = format!("`{s}` is not a valid proxy URL"); + let url = url::Url::parse(s).map_err(|_| s2e(&e))?; + let e = format!("`{s}` does not contain a host"); + let host = url.host_str().ok_or(s2e(&e))?; let mut url_host = String::from(host); - let port = url.port().ok_or(format!("`{s}` does not contain a port"))?; + let e = format!("`{s}` does not contain a port"); + let port = url.port().ok_or(s2e(&e))?; url_host.push(':'); url_host.push_str(port.to_string().as_str()); - let mut addr_iter = url_host - .to_socket_addrs() - .map_err(|_| format!("`{host}` could not be resolved"))?; + let e = format!("`{host}` could not be resolved"); + let mut addr_iter = url_host.to_socket_addrs().map_err(|_| s2e(&e))?; - let addr = addr_iter - .next() - .ok_or(format!("`{host}` does not resolve to a usable IP address"))?; + let e = format!("`{host}` does not resolve to a usable IP address"); + let addr = addr_iter.next().ok_or(s2e(&e))?; let credentials = if url.username() == "" && url.password().is_none() { None @@ -49,7 +50,7 @@ impl Proxy { "http" => Some(ProxyType::Http), _ => None, } - .ok_or(format!("`{scheme}` is an invalid proxy type"))?; + .ok_or(s2e(&format!("`{scheme}` is an invalid proxy type")))?; Ok(Proxy { proxy_type, diff --git a/src/socks5.rs b/src/socks5.rs index 76d5a90..90ab7c2 100644 --- a/src/socks5.rs +++ b/src/socks5.rs @@ -1,6 +1,7 @@ +use crate::error::Error; use crate::tun2proxy::{ Connection, ConnectionManager, Credentials, IncomingDataEvent, IncomingDirection, - OutgoingDataEvent, OutgoingDirection, ProxyError, TcpProxy, + OutgoingDataEvent, OutgoingDirection, TcpProxy, }; use std::collections::VecDeque; use std::net::{IpAddr, SocketAddr}; @@ -90,22 +91,18 @@ impl SocksConnection { self.state = SocksState::ServerHello; } - fn receive_server_hello(&mut self) -> Result<(), ProxyError> { + fn receive_server_hello(&mut self) -> Result<(), Error> { if self.server_inbuf.len() < 2 { return Ok(()); } if self.server_inbuf[0] != 5 { - return Err(ProxyError::new( - "SOCKS server replied with an unexpected version.".into(), - )); + return Err("SOCKS server replied with an unexpected version.".into()); } if self.server_inbuf[1] != 0 && self.manager.get_credentials().is_none() || self.server_inbuf[1] != 2 && self.manager.get_credentials().is_some() { - return Err(ProxyError::new( - "SOCKS server requires an unsupported authentication method.".into(), - )); + return Err("SOCKS server requires an unsupported authentication method.".into()); } self.server_inbuf.drain(0..2); @@ -118,7 +115,7 @@ impl SocksConnection { self.state_change() } - fn send_auth_data(&mut self) -> Result<(), ProxyError> { + fn send_auth_data(&mut self) -> Result<(), Error> { let tmp = Credentials::default(); let credentials = self.manager.get_credentials().as_ref().unwrap_or(&tmp); self.server_outbuf @@ -131,19 +128,19 @@ impl SocksConnection { self.state_change() } - fn receive_auth_data(&mut self) -> Result<(), ProxyError> { + fn receive_auth_data(&mut self) -> Result<(), Error> { if self.server_inbuf.len() < 2 { return Ok(()); } if self.server_inbuf[0] != 1 || self.server_inbuf[1] != 0 { - return Err(ProxyError::new("SOCKS authentication failed.".into())); + return Err("SOCKS authentication failed.".into()); } self.server_inbuf.drain(0..2); self.state = SocksState::SendRequest; self.state_change() } - fn receive_connection_status(&mut self) -> Result<(), ProxyError> { + fn receive_connection_status(&mut self) -> Result<(), Error> { if self.server_inbuf.len() < 4 { return Ok(()); } @@ -153,22 +150,18 @@ impl SocksConnection { let atyp = self.server_inbuf[3]; if ver != 5 { - return Err(ProxyError::new( - "SOCKS server replied with an unexpected version.".into(), - )); + return Err("SOCKS server replied with an unexpected version.".into()); } if rep != 0 { - return Err(ProxyError::new("SOCKS connection unsuccessful.".into())); + return Err("SOCKS connection unsuccessful.".into()); } if atyp != SocksAddressType::Ipv4 as u8 && atyp != SocksAddressType::Ipv6 as u8 && atyp != SocksAddressType::DomainName as u8 { - return Err(ProxyError::new( - "SOCKS server replied with unrecognized address type.".into(), - )); + return Err("SOCKS server replied with unrecognized address type.".into()); } if atyp == SocksAddressType::DomainName as u8 && self.server_inbuf.len() < 5 { @@ -197,7 +190,7 @@ impl SocksConnection { self.state_change() } - fn send_request(&mut self) -> Result<(), ProxyError> { + fn send_request(&mut self) -> Result<(), Error> { let dst_ip = self.connection.dst.ip(); let cmd = if dst_ip.is_ipv4() { 1 } else { 4 }; self.server_outbuf.extend(&[5u8, 1, 0, cmd]); @@ -213,7 +206,7 @@ impl SocksConnection { self.state_change() } - pub fn state_change(&mut self) -> Result<(), ProxyError> { + pub fn state_change(&mut self) -> Result<(), Error> { match self.state { SocksState::ServerHello => self.receive_server_hello(), @@ -239,7 +232,7 @@ impl SocksConnection { } impl TcpProxy for SocksConnection { - fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), ProxyError> { + fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error> { let direction = event.direction; let buffer = event.buffer; match direction { diff --git a/src/tun2proxy.rs b/src/tun2proxy.rs index a2f8da4..4648f07 100644 --- a/src/tun2proxy.rs +++ b/src/tun2proxy.rs @@ -1,3 +1,4 @@ +use crate::error::Error; use crate::virtdevice::VirtualTunDevice; use log::{error, info}; use mio::event::Event; @@ -18,20 +19,6 @@ use std::net::Shutdown::Both; use std::net::{IpAddr, Shutdown, SocketAddr}; use std::os::unix::io::AsRawFd; -pub struct ProxyError { - message: String, -} - -impl ProxyError { - pub fn new(message: String) -> Self { - Self { message } - } - - pub fn message(&self) -> String { - self.message.clone() - } -} - #[derive(Hash, Clone, Copy, Eq, PartialEq)] pub struct Connection { pub src: std::net::SocketAddr, @@ -178,7 +165,7 @@ impl Credentials { } pub(crate) trait TcpProxy { - fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), ProxyError>; + fn push_data(&mut self, event: IncomingDataEvent<'_>) -> Result<(), Error>; fn consume_data(&mut self, dir: OutgoingDirection, size: usize); fn peek_data(&mut self, dir: OutgoingDirection) -> OutgoingDataEvent; fn connection_established(&self) -> bool; @@ -302,10 +289,6 @@ impl<'a> TunToProxy<'a> { None } - fn print_error(error: ProxyError) { - error!("{}", error.message()); - } - fn tunsocket_read_and_forward(&mut self, connection: &Connection) { if let Some(state) = self.connections.get_mut(connection) { let closed = { @@ -329,7 +312,7 @@ impl<'a> TunToProxy<'a> { match error { Ok(_) => socket.state() == smoltcp::socket::tcp::State::CloseWait, Err(e) => { - Self::print_error(e); + log::error!("{e}"); true } } @@ -512,7 +495,7 @@ impl<'a> TunToProxy<'a> { socket.close(); } self.expect_smoltcp_send(); - Self::print_error(error); + log::error! {"{error}"}; self.remove_connection(&connection.clone()); return; }