proxychains_masq/proxy/
http.rs1use anyhow::{bail, Context, Result};
2use tokio::io::{AsyncReadExt, AsyncWriteExt};
3
4use super::{BoxStream, Target};
5
6pub async fn connect(
16 mut stream: BoxStream,
17 target: &Target,
18 username: Option<&str>,
19 password: Option<&str>,
20) -> Result<BoxStream> {
21 let host_port = match target {
22 Target::Ip(addr, port) => format!("{addr}:{port}"),
23 Target::Host(host, port) => format!("{host}:{port}"),
24 };
25
26 let mut req = format!("CONNECT {host_port} HTTP/1.0\r\nHost: {host_port}\r\n");
27
28 if let (Some(u), Some(p)) = (username, password) {
29 let credentials = base64_encode(&format!("{u}:{p}"));
30 req.push_str(&format!("Proxy-Authorization: Basic {credentials}\r\n"));
31 }
32 req.push_str("\r\n");
33
34 stream
35 .write_all(req.as_bytes())
36 .await
37 .context("http: write CONNECT")?;
38
39 let mut response = Vec::<u8>::with_capacity(256);
41 loop {
42 let b = stream.read_u8().await.context("http: read response")?;
43 response.push(b);
44 if response.ends_with(b"\r\n\r\n") {
45 break;
46 }
47 if response.len() > 8192 {
48 bail!("http: response headers too large");
49 }
50 }
51
52 let header_line = response
53 .split(|&b| b == b'\n')
54 .next()
55 .context("http: empty response")?;
56 let header_str = std::str::from_utf8(header_line)
57 .context("http: non-UTF-8 status line")?
58 .trim();
59
60 let status_code: u16 = header_str
62 .split_whitespace()
63 .nth(1)
64 .context("http: missing status code")?
65 .parse()
66 .context("http: invalid status code")?;
67
68 if !(200..300).contains(&status_code) {
69 bail!("http: CONNECT failed with status {status_code}");
70 }
71
72 Ok(stream)
73}
74
75fn base64_encode(s: &str) -> String {
76 const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
77 let input = s.as_bytes();
78 let mut out = String::new();
79 for chunk in input.chunks(3) {
80 let b0 = chunk[0] as u32;
81 let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
82 let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
83 let n = (b0 << 16) | (b1 << 8) | b2;
84 out.push(CHARS[((n >> 18) & 0x3f) as usize] as char);
85 out.push(CHARS[((n >> 12) & 0x3f) as usize] as char);
86 out.push(if chunk.len() > 1 {
87 CHARS[((n >> 6) & 0x3f) as usize] as char
88 } else {
89 '='
90 });
91 out.push(if chunk.len() > 2 {
92 CHARS[(n & 0x3f) as usize] as char
93 } else {
94 '='
95 });
96 }
97 out
98}