handshake.rs
1 //! Implement the socks handshakes. 2 3 #[cfg(any(feature = "proxy-handshake", feature = "client-handshake"))] 4 #[macro_use] 5 pub(crate) mod framework; 6 7 #[cfg(feature = "client-handshake")] 8 pub(crate) mod client; 9 #[cfg(feature = "proxy-handshake")] 10 pub(crate) mod proxy; 11 12 use crate::msg::SocksAddr; 13 use std::net::IpAddr; 14 use tor_bytes::Result as BytesResult; 15 use tor_bytes::{EncodeResult, Error as BytesError, Readable, Reader, Writeable, Writer}; 16 17 /// Constant for Username/Password-style authentication. 18 /// (See RFC 1929) 19 const USERNAME_PASSWORD: u8 = 0x02; 20 /// Constant for "no authentication". 21 const NO_AUTHENTICATION: u8 = 0x00; 22 23 /// An action to take in response to a SOCKS handshake message. 24 #[derive(Clone, Debug)] 25 #[non_exhaustive] 26 pub struct Action { 27 /// If nonzero, this many bytes should be drained from the 28 /// client's inputs. 29 pub drain: usize, 30 /// If nonempty, this reply should be sent to the other party. 31 pub reply: Vec<u8>, 32 /// If true, then this handshake is over, either successfully or not. 33 pub finished: bool, 34 } 35 36 impl Readable for SocksAddr { 37 fn take_from(r: &mut Reader<'_>) -> BytesResult<SocksAddr> { 38 let atype = r.take_u8()?; 39 match atype { 40 1 => { 41 let ip4: std::net::Ipv4Addr = r.extract()?; 42 Ok(SocksAddr::Ip(ip4.into())) 43 } 44 3 => { 45 let hlen = r.take_u8()?; 46 let hostname = r.take(hlen as usize)?; 47 let hostname = std::str::from_utf8(hostname) 48 .map_err(|_| BytesError::InvalidMessage("bad utf8 on hostname".into()))? 49 .to_string(); 50 let hostname = hostname 51 .try_into() 52 .map_err(|_| BytesError::InvalidMessage("hostname too long".into()))?; 53 Ok(SocksAddr::Hostname(hostname)) 54 } 55 4 => { 56 let ip6: std::net::Ipv6Addr = r.extract()?; 57 Ok(SocksAddr::Ip(ip6.into())) 58 } 59 _ => Err(BytesError::InvalidMessage( 60 "unrecognized address type.".into(), 61 )), 62 } 63 } 64 } 65 66 impl Writeable for SocksAddr { 67 fn write_onto<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> { 68 match self { 69 SocksAddr::Ip(IpAddr::V4(ip)) => { 70 w.write_u8(1); 71 w.write(ip)?; 72 } 73 SocksAddr::Ip(IpAddr::V6(ip)) => { 74 w.write_u8(4); 75 w.write(ip)?; 76 } 77 SocksAddr::Hostname(h) => { 78 let h = h.as_ref(); 79 assert!(h.len() < 256); 80 let hlen = h.len() as u8; 81 w.write_u8(3); 82 w.write_u8(hlen); 83 w.write(h.as_bytes())?; 84 } 85 } 86 Ok(()) 87 } 88 } 89 90 #[cfg(all(feature = "client-handshake", feature = "proxy-handshake"))] 91 #[cfg(test)] 92 mod test_roundtrip { 93 // @@ begin test lint list 94 #![allow(clippy::bool_assert_comparison)] 95 #![allow(clippy::clone_on_copy)] 96 #![allow(clippy::dbg_macro)] 97 #![allow(clippy::mixed_attributes_style)] 98 #![allow(clippy::print_stderr)] 99 #![allow(clippy::print_stdout)] 100 #![allow(clippy::single_char_pattern)] 101 #![allow(clippy::unwrap_used)] 102 #![allow(clippy::unchecked_duration_subtraction)] 103 #![allow(clippy::useless_vec)] 104 #![allow(clippy::needless_pass_by_value)] 105 //! <!-- @@ end test lint list 106 107 use crate::*; 108 use std::collections::VecDeque; 109 110 /// Given a socks request, run a complete (successful round) trip, reply with the 111 /// the given status code, and return both sides' results. 112 /// 113 /// Use the (deprecated) `Handshake::handshake` and `Action` API 114 fn run_handshake_old_api( 115 request: SocksRequest, 116 status: SocksStatus, 117 ) -> (SocksRequest, SocksReply) { 118 let mut client_hs = SocksClientHandshake::new(request); 119 let mut proxy_hs = SocksProxyHandshake::new(); 120 let mut received_request = None; 121 122 let mut last_proxy_msg = vec![]; 123 // Prevent infinite loop in case of bugs. 124 for _ in 0..100 { 125 // Make sure that the client says "truncated" for all prefixes of the proxy's message. 126 for truncate in 0..last_proxy_msg.len() { 127 let r = client_hs.handshake_for_tests(&last_proxy_msg[..truncate]); 128 assert!(r.is_err()); 129 } 130 // Get the client's actual message. 131 let client_action = client_hs 132 .handshake_for_tests(&last_proxy_msg) 133 .unwrap() 134 .unwrap(); 135 assert_eq!(client_action.drain, last_proxy_msg.len()); 136 if client_action.finished { 137 let received_reply = client_hs.into_reply(); 138 return (received_request.unwrap(), received_reply.unwrap()); 139 } 140 let client_msg = client_action.reply; 141 142 // Make sure that the proxy says "truncated" for all prefixes of the client's message. 143 for truncate in 0..client_msg.len() { 144 let r = proxy_hs.handshake_for_tests(&client_msg[..truncate]); 145 assert!(r.is_err()); 146 } 147 // Get the proxy's actual reply (if any). 148 let proxy_action = proxy_hs.handshake_for_tests(&client_msg).unwrap().unwrap(); 149 assert_eq!(proxy_action.drain, client_msg.len()); 150 last_proxy_msg = if proxy_action.finished { 151 // The proxy is done: have it reply with a status code. 152 received_request = proxy_hs.clone().into_request(); 153 received_request 154 .as_ref() 155 .unwrap() 156 .reply(status, None) 157 .unwrap() 158 } else { 159 proxy_action.reply 160 }; 161 } 162 panic!("Handshake ran for too many steps") 163 } 164 165 /// Given a socks request, run a complete (successful round) trip, reply with the 166 /// the given status code, and return both sides' results. 167 /// 168 /// Use the (new) `Handshake::step` API 169 fn run_handshake_new_api<P: ReadPrecision, const MAX_RECV: usize>( 170 request: SocksRequest, 171 status: SocksStatus, 172 ) -> (SocksRequest, SocksReply) { 173 struct State<P: ReadPrecision, H: Handshake> { 174 hs: H, 175 buf: Buffer<P>, 176 fin: Option<H::Output>, 177 } 178 179 struct DidSomething; 180 181 let mut client = State::<P, _>::new(SocksClientHandshake::new(request)); 182 let mut server = State::<P, _>::new(SocksProxyHandshake::new()); 183 184 let mut c2s = VecDeque::new(); 185 let mut s2c = VecDeque::new(); 186 187 let mut status = Some(status); 188 189 impl<P: ReadPrecision, H: Handshake> State<P, H> { 190 fn new(hs: H) -> Self { 191 State { 192 hs, 193 buf: Default::default(), 194 fin: None, 195 } 196 } 197 198 fn progress_1( 199 &mut self, 200 max_recv: usize, 201 rx: &mut VecDeque<u8>, 202 tx: &mut VecDeque<u8>, 203 ) -> Option<DidSomething> { 204 use NextStep as NS; 205 206 if self.fin.is_some() { 207 return None; 208 } 209 210 match self.hs.step(&mut self.buf).unwrap() { 211 NS::Recv(mut recv) => { 212 let n = [recv.buf().len(), rx.len(), max_recv] 213 .into_iter() 214 .min() 215 .unwrap(); 216 for p in &mut recv.buf()[0..n] { 217 *p = rx.pop_front().unwrap(); 218 } 219 recv.note_received(n).unwrap_or_else(|e| match e { 220 // This is actually expected; our test case produces 0-byte reads 221 // sometimes. 222 Error::UnexpectedEof => {} 223 other => panic!("{:?}", other), 224 }); 225 if n != 0 { 226 Some(DidSomething) 227 } else { 228 None 229 } 230 } 231 NS::Send(send) => { 232 for c in send { 233 tx.push_back(c); 234 } 235 Some(DidSomething) 236 } 237 NS::Finished(fin) => { 238 self.fin = Some(fin.into_output_forbid_pipelining().unwrap()); 239 Some(DidSomething) 240 } 241 } 242 } 243 } 244 245 loop { 246 let ds = [ 247 client.progress_1(MAX_RECV, &mut s2c, &mut c2s), 248 server.progress_1(MAX_RECV, &mut c2s, &mut s2c), 249 ] 250 .into_iter() 251 .flatten() 252 .next(); 253 254 if let Some(DidSomething) = ds { 255 continue; 256 } 257 258 let Some(status) = status.take() else { break }; 259 260 let reply = server.fin.as_ref().unwrap().reply(status, None).unwrap(); 261 for c in reply { 262 s2c.push_back(c); 263 } 264 } 265 266 (server.fin.unwrap(), client.fin.unwrap()) 267 } 268 269 // Invoke run_handshake and assert that the output matches the input. 270 fn test_handshake(request: &SocksRequest, status: SocksStatus) { 271 for run_handshake in [ 272 run_handshake_old_api, 273 run_handshake_new_api::<(), 1>, 274 run_handshake_new_api::<(), 100>, 275 run_handshake_new_api::<PreciseReads, 1>, 276 run_handshake_new_api::<PreciseReads, 100>, 277 ] { 278 let (request_out, status_out) = run_handshake(request.clone(), status); 279 assert_eq!(&request_out, request); 280 assert_eq!(status_out.status(), status); 281 } 282 } 283 284 #[test] 285 fn socks4() { 286 test_handshake( 287 &SocksRequest::new( 288 SocksVersion::V4, 289 SocksCmd::CONNECT, 290 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()), 291 443, 292 SocksAuth::NoAuth, 293 ) 294 .unwrap(), 295 SocksStatus::SUCCEEDED, 296 ); 297 298 test_handshake( 299 &SocksRequest::new( 300 SocksVersion::V4, 301 SocksCmd::CONNECT, 302 SocksAddr::Ip("192.0.2.33".parse().unwrap()), 303 22, 304 SocksAuth::Socks4(b"swordfish".to_vec()), 305 ) 306 .unwrap(), 307 SocksStatus::GENERAL_FAILURE, 308 ); 309 } 310 311 #[test] 312 fn socks5() { 313 test_handshake( 314 &SocksRequest::new( 315 SocksVersion::V5, 316 SocksCmd::CONNECT, 317 SocksAddr::Hostname("www.torproject.org".to_string().try_into().unwrap()), 318 443, 319 SocksAuth::NoAuth, 320 ) 321 .unwrap(), 322 SocksStatus::SUCCEEDED, 323 ); 324 325 test_handshake( 326 &SocksRequest::new( 327 SocksVersion::V5, 328 SocksCmd::CONNECT, 329 SocksAddr::Ip("2001:db8::32".parse().unwrap()), 330 443, 331 SocksAuth::Username(b"belbo".to_vec(), b"non".to_vec()), 332 ) 333 .unwrap(), 334 SocksStatus::GENERAL_FAILURE, 335 ); 336 } 337 }