Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions phantun/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ tokio-tun = "0.11"
num_cpus = "1.13"
neli = "0.6"
nix = { version = "0.28", features = ["net"] }
dns-lookup = "2.0.4"
23 changes: 14 additions & 9 deletions phantun/src/bin/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clap::{crate_version, Arg, ArgAction, Command};
use fake_tcp::packet::MAX_PACKET_LEN;
use fake_tcp::{Socket, Stack};
use log::{debug, error, info};
use phantun::utils::{assign_ipv6_address, new_udp_reuseport};
use phantun::utils::{assign_ipv6_address, new_udp_reuseport, lookup_host};
use std::collections::HashMap;
use std::fs;
use std::io;
Expand Down Expand Up @@ -35,8 +35,8 @@ async fn main() -> io::Result<()> {
.short('r')
.long("remote")
.required(true)
.value_name("IP or HOST NAME:PORT")
.help("Sets the address or host name and port where Phantun Client connects to Phantun Server, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
.value_name("IP:PORT or HOST_NAME:PORT or IP4P_DOMIAN:0")
.help("Sets the address or host name and port where Phantun Client connects to Phantun Server, IPv6 address need to be specified as: \"[IPv6]:PORT\", IP4P Domain need to be specified as: \"DOMAIN:0\"")
)
.arg(
Arg::new("tun")
Expand Down Expand Up @@ -101,6 +101,14 @@ async fn main() -> io::Result<()> {
Note: ensure this file's size does not exceed the MTU of the outgoing interface. \
The content is always sent out in a single packet and will not be further segmented")
)
.arg(
Arg::new("ip4p_remote")
.long("ip4p-remote")
.short('n')
.required(false)
.action(ArgAction::SetTrue)
.help("Use IP4P domain to resolve remote address")
)
.get_matches();

let local_addr: SocketAddr = matches
Expand All @@ -111,12 +119,9 @@ async fn main() -> io::Result<()> {

let ipv4_only = matches.get_flag("ipv4_only");

let remote_addr = tokio::net::lookup_host(matches.get_one::<String>("remote").unwrap())
.await
.expect("bad remote address or host")
.find(|addr| !ipv4_only || addr.is_ipv4())
.expect("unable to resolve remote host name");
info!("Remote address is: {}", remote_addr);
let ip4p_resolve = matches.get_flag("ip4p_remote");

let remote_addr = lookup_host(matches.get_one::<String>("remote").unwrap(), ip4p_resolve, ipv4_only).await;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only resolve the DNS once on program start but the use case for IP4P usually means both port and IP is dynamic and would require update from time to time.


let tun_local: Ipv4Addr = matches
.get_one::<String>("tun_local")
Expand Down
36 changes: 35 additions & 1 deletion phantun/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use neli::{
socket::NlSocketHandle,
types::RtBuffer,
};
use std::net::{Ipv6Addr, SocketAddr};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
use tokio::net::UdpSocket;

pub fn new_udp_reuseport(local_addr: SocketAddr) -> UdpSocket {
Expand Down Expand Up @@ -58,3 +58,37 @@ pub fn assign_ipv6_address(device_name: &str, local: Ipv6Addr, peer: Ipv6Addr) {
);
rtnl.send(nl_header).unwrap();
}

pub async fn lookup_host(domain: &str, ip4p_resolve: bool, ipv4_only: bool) -> std::net::SocketAddr {
if ip4p_resolve {
return resolve_ip4p_domain(domain);
}
return tokio::net::lookup_host(domain)
.await
.expect("bad remote address or host")
.find(|addr| !ipv4_only || addr.is_ipv4())
.expect("unable to resolve remote host name");
}

fn resolve_ip4p_domain(domain: &str) -> std::net::SocketAddr {
let ip4p_addr = dns_lookup::lookup_host(domain.trim_end_matches(":0")).unwrap();
let ip4p_addr = ip4p_addr[0].to_string();
let ip4p_resolve: Vec<&str> = ip4p_addr.split(':').collect();
if ip4p_resolve.len() != 5 {
panic!("Invalid IP4P values: {:?}", ip4p_resolve);
}
let port = u16::from_str_radix(ip4p_resolve[2], 16).expect("Invalid port value");
let ipab = u16::from_str_radix(ip4p_resolve[3], 16).expect("Invalid ipab value");
let ipcd = u16::from_str_radix(ip4p_resolve[4], 16).expect("Invalid ipcd value");

let ipa = ipab >> 8;
let ipb = ipab & 0xff;
let ipc = ipcd >> 8;
let ipd = ipcd & 0xff;

let remote_addr = SocketAddr::new(
Ipv4Addr::new(ipa.try_into().unwrap(), ipb.try_into().unwrap(), ipc.try_into().unwrap(), ipd.try_into().unwrap()).into(),
port.try_into().unwrap()
);
return remote_addr;
}