use super::HttpClientConfig;
use crate::error::Error;
use std::io::{Read, Write};
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
use url::Url;
pub fn connect(
sock: &mut TcpStream,
config: &HttpClientConfig,
uri: &Url,
port: &u16,
) -> Result<(), Error> {
self::hello(sock, config)?;
self::request(sock, uri, port)?;
Ok(())
}
fn hello(sock: &mut TcpStream, config: &HttpClientConfig) -> Result<(), Error> {
sock.write_all(&[0x05, 0x01, 0x00]).unwrap();
sock.flush().unwrap();
let mut buffer = [0u8; 2];
sock.read(&mut buffer).unwrap();
if buffer[1] == 0xFF {
return Err(Error::Custom(
"SOCKS5 gave invalid response after initial greeting, no auth methods available."
.to_string(),
));
} else if buffer[1] == 0x02 {
self::authenticate(sock, config)?;
return Err( Error::Custom("Authentication required, but not developed in for atlas-http. Please raise issue on Github and bug developer, https://github.com/mdizak/rust-atlas-http".to_string()) );
}
Ok(())
}
fn authenticate(sock: &mut TcpStream, config: &HttpClientConfig) -> Result<(), Error> {
let mut request = vec![0x01];
request.push(config.proxy_user.len() as u8);
for c in config.proxy_user.chars() {
request.push(c as u8);
}
request.push(config.proxy_password.len() as u8);
for c in config.proxy_password.chars() {
request.push(c as u8);
}
sock.write_all(&request).unwrap();
sock.flush().unwrap();
let mut buffer = [0u8; 2];
sock.read(&mut buffer).unwrap();
if buffer[1] != 0x00 {
return Err(Error::Custom(
"SOCKS5 proxy authentication failed. Please check proxy user / pass, and try again."
.to_string(),
));
}
Ok(())
}
fn request(sock: &mut TcpStream, uri: &Url, port: &u16) -> Result<(), Error> {
let hostname = format!("{}:{}", uri.host_str().unwrap(), port);
let mut address = hostname.to_socket_addrs().unwrap();
let addr = address.next().unwrap();
let mut request = vec![0x05, 0x01, 0x00];
if let SocketAddr::V6(h) = addr {
request.push(0x04);
for byte in h.ip().octets() {
request.push(byte);
}
} else if let SocketAddr::V4(h) = addr {
request.push(0x01);
for byte in h.ip().octets() {
request.push(byte);
}
} else {
let host = uri.host_str().unwrap();
request.push(0x03);
request.push(host.len() as u8);
for c in host.chars() {
request.push(c as u8);
}
}
request.push((addr.port() >> 8) as u8);
request.push((addr.port() & 0x00FF) as u8);
sock.write_all(&request).unwrap();
sock.flush().unwrap();
let mut buffer = [0u8; 10];
sock.read(&mut buffer).unwrap();
if buffer[3] == 0x04 {
let mut tmp_buffer = [0u8; 12];
sock.read(&mut tmp_buffer).unwrap();
}
if buffer[1] != 0x00 {
return Err(Error::Custom(
"Invalid response from SOCKS5 proxy after 'connect' command.".to_string(),
));
}
Ok(())
}