use anyhow::{bail, Context, Result};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use super::{BoxStream, Target};
pub async fn connect(
mut stream: BoxStream,
target: &Target,
username: Option<&str>,
) -> Result<BoxStream> {
let port = target.port();
let user_bytes = username.unwrap_or("").as_bytes();
let mut req: Vec<u8> = Vec::with_capacity(32);
req.push(4); req.push(1);
req.extend_from_slice(&port.to_be_bytes());
match target {
Target::Ip(addr, _) => {
match addr {
std::net::IpAddr::V4(v4) => req.extend_from_slice(&v4.octets()),
std::net::IpAddr::V6(_) => bail!("SOCKS4 does not support IPv6 addresses"),
}
req.extend_from_slice(user_bytes);
req.push(0); }
Target::Host(hostname, _) => {
req.extend_from_slice(&[0, 0, 0, 1]);
req.extend_from_slice(user_bytes);
req.push(0); req.extend_from_slice(hostname.as_bytes());
req.push(0); }
}
stream
.write_all(&req)
.await
.context("socks4: write request")?;
let mut resp = [0u8; 8];
stream
.read_exact(&mut resp)
.await
.context("socks4: read response")?;
if resp[0] != 0 {
bail!("socks4: unexpected version byte in response: {}", resp[0]);
}
if resp[1] != 90 {
bail!("socks4: connection denied (status {})", resp[1]);
}
Ok(stream)
}