extern crate futures;
extern crate tokio_core;
extern crate tokio_io;
use std::io;
use std::str;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use futures::future;
use futures::Future;
use tokio_io::io::{read_exact, write_all};
use tokio_core::net::TcpStream;
mod v5 {
pub const VERSION: u8 = 5;
pub const METH_NO_AUTH: u8 = 0;
pub const CMD_CONNECT: u8 = 1;
pub const TYPE_IPV4: u8 = 1;
pub const TYPE_IPV6: u8 = 4;
pub const TYPE_DOMAIN: u8 = 3;
}
pub fn serve(conn: TcpStream) -> Box<Future<Item = (TcpStream, String), Error = io::Error>> {
let version = read_exact(conn, [0u8; 2]).and_then(|(conn, buf)| if buf[0] == v5::VERSION {
Ok((conn, buf))
} else {
Err(other("unknown version"))
});
let method = version.and_then(|(conn, buf)| read_exact(conn, [0u8, buf[1] as u8]));
let part1 = method.and_then(|(conn, _)| write_all(conn, [v5::VERSION, v5::METH_NO_AUTH]));
let ack = part1.and_then(|(conn, _)| {
read_exact(conn, [0u8]).and_then(|(conn, buf)| if buf[0] == v5::VERSION {
Ok(conn)
} else {
Err(other("didn't confirm with v5 version"))
})
});
let command = ack.and_then(|conn| {
read_exact(conn, [0u8]).and_then(|(conn, buf)| if buf[0] == v5::CMD_CONNECT {
Ok(conn)
} else {
Err(other("unsupported command"))
})
});
let resv = command.and_then(|c| read_exact(c, [0u8]));
let adress_type = resv.and_then(|(conn, _)| read_exact(conn, [0u8]));
let address = mybox(adress_type.and_then(move |(c, buf)| {
match buf[0] {
v5::TYPE_IPV4 => mybox(read_exact(c, [0u8; 6]).and_then(|(c, buf)| {
let addr = Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3]);
let port = ((buf[4] as u16) << 8) | (buf[5] as u16);
let addr = SocketAddrV4::new(addr, port);
mybox(future::ok((c, format!("{}", addr))))
})),
v5::TYPE_IPV6 => mybox(read_exact(c, [0u8; 18]).and_then(|(conn, buf)| {
let a = ((buf[0] as u16) << 8) | (buf[1] as u16);
let b = ((buf[2] as u16) << 8) | (buf[3] as u16);
let c = ((buf[4] as u16) << 8) | (buf[5] as u16);
let d = ((buf[6] as u16) << 8) | (buf[7] as u16);
let e = ((buf[8] as u16) << 8) | (buf[9] as u16);
let f = ((buf[10] as u16) << 8) | (buf[11] as u16);
let g = ((buf[12] as u16) << 8) | (buf[13] as u16);
let h = ((buf[14] as u16) << 8) | (buf[15] as u16);
let addr = Ipv6Addr::new(a, b, c, d, e, f, g, h);
let port = ((buf[16] as u16) << 8) | (buf[17] as u16);
let addr = SocketAddrV6::new(addr, port, 0, 0);
mybox(future::ok((conn, format!("{}", addr))))
})),
v5::TYPE_DOMAIN => mybox(
read_exact(c, [0u8])
.and_then(|(conn, buf)| {
read_exact(conn, vec![0u8; buf[0] as usize + 2])
})
.and_then(|(conn, buf)| {
let hostname = &buf[..buf.len() - 2];
let hostname = if let Ok(hostname) = str::from_utf8(hostname) {
hostname
} else {
return mybox(future::err(other("hostname include invalid utf8")));
};
let pos = buf.len() - 2;
let port = ((buf[pos] as u16) << 8) | (buf[pos + 1] as u16);
mybox(future::ok((conn, format!("{}:{}", hostname.to_string(), port))))
}),
),
n => {
let msg = format!("unknown address type, received: {}", n);
mybox(future::err(other(&msg)))
}
}
}));
let handshake_finish = mybox(address.and_then(move |(conn, addr)| {
let resp = [0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43];
write_all(conn, resp).map(move |(conn, _)| (conn, addr))
}));
mybox(
handshake_finish.and_then(|(conn, addr)| future::ok((conn, addr))),
)
}
fn mybox<F: Future + 'static>(f: F) -> Box<Future<Item = F::Item, Error = F::Error>> {
Box::new(f)
}
fn other(desc: &str) -> io::Error {
io::Error::new(io::ErrorKind::Other, desc)
}