use socks::{Socks5Datagram, TargetAddr};
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::Duration;
pub fn lookup_host_v4(host: &str) -> io::Result<Ipv4Addr> {
dns_lookup::lookup_host(host)?
.into_iter()
.map(|addr| match addr {
IpAddr::V4(ip) => Some(ip),
_ => None,
})
.filter(|addr| addr.is_some())
.map(|addr| addr.unwrap())
.next()
.ok_or(io::Error::from(io::ErrorKind::NotFound))
}
pub trait RW: Send + Sync {
fn local_addr(&self) -> io::Result<SocketAddrV4>;
fn send_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<usize>;
fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddrV4)>;
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>;
fn read_timeout(&self) -> io::Result<Option<Duration>>;
fn write_timeout(&self) -> io::Result<Option<Duration>>;
}
#[derive(Debug)]
pub struct Datagram {
datagram: Socks5Datagram,
}
impl Datagram {
pub fn bind(
proxy: SocketAddrV4,
addr: SocketAddrV4,
auth: Option<(String, String)>,
) -> io::Result<Datagram> {
let datagram = match auth {
Some((username, password)) => Socks5Datagram::bind_with_password(
proxy,
addr,
username.as_str(),
password.as_str(),
)?,
None => Socks5Datagram::bind(proxy, addr)?,
};
Ok(Datagram { datagram })
}
}
impl RW for Datagram {
fn local_addr(&self) -> io::Result<SocketAddrV4> {
let addr = self.datagram.get_ref().local_addr()?;
match addr {
SocketAddr::V4(addr) => Ok(addr),
_ => unreachable!(),
}
}
fn send_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<usize> {
let size = self.datagram.send_to(buf, addr)?;
Ok(size)
}
fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddrV4)> {
let (size, addr) = self.datagram.recv_from(buf)?;
return match addr {
TargetAddr::Ip(addr) => match addr {
SocketAddr::V4(addr) => Ok((size, addr)),
_ => unreachable!(),
},
_ => unreachable!(),
};
}
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.datagram.get_ref().set_read_timeout(dur)?;
Ok(())
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.datagram.get_ref().set_write_timeout(dur)?;
Ok(())
}
fn read_timeout(&self) -> io::Result<Option<Duration>> {
let duration = self.datagram.get_ref().read_timeout()?;
Ok(duration)
}
fn write_timeout(&self) -> io::Result<Option<Duration>> {
let duration = self.datagram.get_ref().write_timeout()?;
Ok(duration)
}
}
#[derive(Debug)]
pub struct Socket {
socket: UdpSocket,
}
impl Socket {
pub fn bind(addr: SocketAddrV4) -> io::Result<Socket> {
let socket = UdpSocket::bind(addr)?;
Ok(Socket { socket })
}
}
impl RW for Socket {
fn local_addr(&self) -> io::Result<SocketAddrV4> {
let addr = self.socket.local_addr()?;
match addr {
SocketAddr::V4(addr) => Ok(addr),
_ => unreachable!(),
}
}
fn send_to(&self, buf: &[u8], addr: SocketAddrV4) -> io::Result<usize> {
let size = self.socket.send_to(buf, addr)?;
Ok(size)
}
fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddrV4)> {
let (size, addr) = self.socket.recv_from(buf)?;
match addr {
SocketAddr::V4(addr) => Ok((size, addr)),
_ => unreachable!(),
}
}
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.socket.set_read_timeout(dur)?;
Ok(())
}
fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.socket.set_write_timeout(dur)?;
Ok(())
}
fn read_timeout(&self) -> io::Result<Option<Duration>> {
let duration = self.socket.read_timeout()?;
Ok(duration)
}
fn write_timeout(&self) -> io::Result<Option<Duration>> {
let duration = self.socket.write_timeout()?;
Ok(duration)
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum NatType {
A,
B,
C,
D,
F,
}
impl NatType {
pub fn nintendo(&self) -> String {
self.to_string()
}
pub fn sony(&self) -> String {
match self {
NatType::A => "1".to_string(),
NatType::B => "2".to_string(),
NatType::C => "3".to_string(),
NatType::D => "3".to_string(),
NatType::F => "-".to_string(),
}
}
pub fn microsoft(&self) -> String {
match self {
NatType::A => "Open".to_string(),
NatType::B => "Moderate".to_string(),
NatType::C => "Strict".to_string(),
NatType::D => "Strict".to_string(),
NatType::F => "Unavailable".to_string(),
}
}
}
impl Display for NatType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NatType::A => write!(f, "A"),
NatType::B => write!(f, "B"),
NatType::C => write!(f, "C"),
NatType::D => write!(f, "D"),
NatType::F => write!(f, "F"),
}
}
}
const PAYLOAD_1: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const PAYLOAD_2: [u8; 16] = [0, 0, 0, 0x65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const PAYLOAD_3: [u8; 16] = [0, 0, 0, 0x66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
const PAYLOAD_4: [u8; 16] = [0, 0, 0, 0x67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
#[allow(dead_code)]
#[derive(Clone, Debug)]
struct Response {
payload: [u8; 4],
reserved: [u8; 2],
port: u16,
remote_ip: Ipv4Addr,
local_ip: Ipv4Addr,
}
impl Response {
fn unique_number(&self) -> u8 {
self.payload[3]
}
fn is_payload_2(&self) -> bool {
self.payload[3] == 0x65
}
fn is_payload_3(&self) -> bool {
self.payload[3] == 0x66
}
fn is_payload_4(&self) -> bool {
self.payload[3] == 0x67
}
#[allow(dead_code)]
fn port(&self) -> u16 {
self.port
}
#[allow(dead_code)]
fn remote_ip(&self) -> Ipv4Addr {
self.remote_ip
}
#[allow(dead_code)]
fn local_ip(&self) -> Ipv4Addr {
self.local_ip
}
fn remote_addr(&self) -> SocketAddrV4 {
SocketAddrV4::new(self.remote_ip, self.port)
}
}
impl From<[u8; 16]> for Response {
fn from(s: [u8; 16]) -> Self {
let [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = s;
Response {
payload: [a, b, c, d],
reserved: [e, f],
port: u16::from_be_bytes([g, h]),
remote_ip: Ipv4Addr::new(i, j, k, l),
local_ip: Ipv4Addr::new(m, n, o, p),
}
}
}
impl TryFrom<&[u8]> for Response {
type Error = io::Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value.len() {
16 => {
let mut s = [0u8; 16];
s.clone_from_slice(&value);
Ok(Response::from(s))
}
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
}
}
}
impl Display for Response {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.unique_number(), self.remote_addr())
}
}
const PORT_1: u16 = 33334;
const PORT_2: u16 = 10025;
const PORT_3: u16 = 50920;
const ONE_TIME_SEND: usize = 5;
pub fn test(
rw: &Box<dyn RW>,
server1: Ipv4Addr,
server2: Ipv4Addr,
) -> io::Result<(SocketAddrV4, SocketAddrV4, bool)> {
let addr_1_1 = SocketAddrV4::new(server1, PORT_1);
let addr_1_2 = SocketAddrV4::new(server1, PORT_2);
let addr_1_3 = SocketAddrV4::new(server1, PORT_3);
let addr_2 = SocketAddrV4::new(server2, PORT_2);
for _ in 0..ONE_TIME_SEND {
rw.send_to(&PAYLOAD_1, addr_1_1)?;
}
for _ in 0..ONE_TIME_SEND {
rw.send_to(&PAYLOAD_2, addr_1_2)?;
}
for _ in 0..ONE_TIME_SEND {
rw.send_to(&PAYLOAD_3, addr_1_2)?;
}
for _ in 0..ONE_TIME_SEND {
rw.send_to(&PAYLOAD_4, addr_2)?;
}
let mut remote1 = None;
let mut remote2 = None;
let mut is_a = false;
let mut buffer = vec![0u8; u16::MAX as usize];
loop {
match rw.recv_from(buffer.as_mut_slice()) {
Ok((size, addr)) => {
if size == 16 {
if addr == addr_1_2 {
let resp = Response::try_from(&buffer[..16]).unwrap();
if resp.is_payload_2() {
remote1 = Some(resp.remote_addr());
}
} else if addr == addr_1_3 {
let resp = Response::try_from(&buffer[..16]).unwrap();
if resp.is_payload_3() {
is_a = true;
}
} else if addr == addr_2 {
let resp = Response::try_from(&buffer[..16]).unwrap();
if resp.is_payload_4() {
remote2 = Some(resp.remote_addr());
}
}
if remote1.is_some() && remote2.is_some() && is_a {
break;
}
}
}
Err(e) => {
if remote1.is_some() && remote2.is_some() {
break;
}
return Err(e);
}
}
}
Ok((remote1.unwrap(), remote2.unwrap(), is_a))
}
pub fn nat_test(
rw1: &Box<dyn RW>,
rw2: &Box<dyn RW>,
server1: Ipv4Addr,
server2: Ipv4Addr,
) -> io::Result<(Option<Ipv4Addr>, NatType)> {
let (remote1, remote2, is_a) = match test(rw1, server1, server2) {
Ok((remote1, remote2, is_a)) => (remote1, remote2, is_a),
Err(e) => match e.kind() {
io::ErrorKind::TimedOut => return Ok((None, NatType::F)),
_ => return Err(e),
},
};
let ip = remote1.ip().clone();
let port_a1 = remote1.port();
let port_b1 = remote2.port();
let nat = match port_a1 == port_b1 {
true => match is_a {
true => NatType::A,
false => NatType::B,
},
false => {
let (remote1, remote2) = match test(rw2, server1, server2) {
Ok((remote1, remote2, _)) => (remote1, remote2),
Err(e) => match e.kind() {
io::ErrorKind::TimedOut => return Ok((None, NatType::F)),
_ => return Err(e),
},
};
let port_a2 = remote1.port();
let port_b2 = remote2.port();
match port_a2
.checked_sub(port_a1)
.unwrap_or_else(|| u16::MAX - (port_a1 - port_a2))
== port_b2
.checked_sub(port_b1)
.unwrap_or_else(|| u16::MAX - (port_b1 - port_b2))
{
true => NatType::C,
false => NatType::D,
}
}
};
Ok((Some(ip), nat))
}