use std::net::Ipv4Addr;
use std::sync::Arc;
use std::time::Duration;
use anyhow::{Context, Result};
use clap::Parser;
use log::{debug, info, warn};
use tokio::net::UdpSocket;
use tokio::sync::mpsc;
use shadowvpn::config::{ClientArgs, ClientConfig, TunConfig};
use shadowvpn::crypto::{decrypt_packet, encrypt_packet, Cipher};
use shadowvpn::mesh::{self, RouteAdvert, RouteInstaller};
use shadowvpn::obfs::{self, Obfuscator};
use shadowvpn::protocol::{max_datagram_size, MAX_IP_PACKET};
use shadowvpn::tun_device::TunDevice;
const CHANNEL_DEPTH: usize = 1024;
const TRANSIENT_RETRY_DELAY: Duration = Duration::from_millis(100);
fn keepalive_payload(tun_ip: Ipv4Addr) -> [u8; 5] {
let [a, b, c, d] = tun_ip.octets();
[0u8, a, b, c, d]
}
#[tokio::main]
async fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let args = ClientArgs::parse();
if args.restore_dns {
if !shadowvpn::policy::dnsconf::restore_from_journal()? {
info!("no DNS restore journal found; nothing to do");
}
return Ok(());
}
let cfg = args
.resolve()
.context("failed to resolve client configuration")?;
run(cfg).await
}
async fn run(cfg: ClientConfig) -> Result<()> {
let cipher = cfg.cipher;
let master_key: Arc<[u8]> = Arc::from(cfg.master_key.into_boxed_slice());
let obfuscator: Option<Arc<Obfuscator>> = cfg
.obfs
.as_deref()
.and_then(Obfuscator::from_name)
.map(Arc::new);
if let Some(name) = cfg.obfs.as_deref() {
info!("carrier obfuscation: {name}");
}
let socket = shadowvpn::net::bind_udp("0.0.0.0:0".parse().expect("valid bind address"))
.context("failed to bind local UDP socket")?;
let server_addr = shadowvpn::net::resolve_server(
&cfg.server,
&[cfg.policy.dns_remote, cfg.policy.dns_local],
cfg.policy.dns_timeout,
)
.await
.with_context(|| format!("failed to resolve server address {}", cfg.server))?;
if server_addr.to_string() != cfg.server {
info!("resolved server {} -> {server_addr}", cfg.server);
}
socket.connect(server_addr).await.with_context(|| {
format!(
"failed to connect UDP socket to server {} ({server_addr})",
cfg.server
)
})?;
let direct_src = socket
.local_addr()
.map(|a| a.ip())
.unwrap_or(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED));
let local_addr = socket
.local_addr()
.map(|a| a.to_string())
.unwrap_or_else(|_| "<unknown>".to_string());
info!("UDP socket {local_addr} connected to server {}", cfg.server);
let socket = Arc::new(socket);
let tun = TunDevice::create(&cfg.tun).with_context(|| {
format!(
"failed to create TUN device (need root/elevated privileges); \
requested ip={} peer={} mtu={}",
cfg.tun.ip, cfg.tun.peer_ip, cfg.tun.mtu
)
})?;
let tun = Arc::new(tun);
let iface_name = tun.name().unwrap_or_else(|_| {
cfg.tun
.name
.clone()
.unwrap_or_else(|| "<unknown>".to_string())
});
info!(
"TUN up: iface={iface_name} ip={} peer={} netmask={} mtu={}",
cfg.tun.ip, cfg.tun.peer_ip, cfg.tun.netmask, cfg.tun.mtu
);
let mut policy_handle = if cfg.policy.mode.is_enabled() {
info!(
"policy routing mode = {}; only matched destinations are tunneled",
cfg.policy.mode.name()
);
Some(
shadowvpn::policy::spawn(
&cfg.policy,
&iface_name,
cfg.tun.ip,
server_addr.ip(),
direct_src,
)
.await
.context("failed to start policy routing")?,
)
} else {
print_routing_hint(&cfg.tun, &cfg.server);
None
};
let mesh_active = cfg.accept_routes || !cfg.advertise_routes.is_empty();
if !cfg.advertise_routes.is_empty() {
info!(
"advertising subnet routes to the server: {}",
cfg.advertise_routes
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
);
}
let route_installer = if cfg.accept_routes {
info!("accepting subnet routes pushed by the server");
Some(Arc::new(
RouteInstaller::new(&iface_name, cfg.tun.ip, server_addr.ip())
.context("setting up the mesh route installer")?,
))
} else {
None
};
let _route_guard = route_installer.clone().map(mesh::InstallerGuard::new);
let periodic_payload: Vec<u8> = if mesh_active {
RouteAdvert {
tunnel_ip: cfg.tun.ip,
tunnel_ip6: cfg.tun.ip6.map(|net| net.ip()),
accept_routes: cfg.accept_routes,
routes: cfg.advertise_routes.clone(),
}
.encode()
} else {
keepalive_payload(cfg.tun.ip).to_vec()
};
let up = tokio::spawn(tun_to_net(
Arc::clone(&tun),
Arc::clone(&socket),
cipher,
Arc::clone(&master_key),
obfuscator.clone(),
));
let down = tokio::spawn(net_to_tun(
Arc::clone(&tun),
Arc::clone(&socket),
cipher,
Arc::clone(&master_key),
obfuscator.clone(),
route_installer,
));
let keepalive = tokio::spawn(keepalive_loop(
Arc::clone(&socket),
cipher,
Arc::clone(&master_key),
obfuscator.clone(),
cfg.keepalive,
periodic_payload,
));
let policy_fut = async {
if let Some(handle) = policy_handle.as_mut() {
return match (&mut handle.task).await {
Ok(inner) => inner.context("DNS proxy loop failed"),
Err(join) => Err(anyhow::Error::new(join).context("DNS proxy task panicked")),
};
}
std::future::pending::<Result<()>>().await
};
tokio::pin!(policy_fut);
tokio::select! {
r = up => propagate("tun->net", r),
r = down => propagate("net->tun", r),
r = keepalive => propagate("keepalive", r),
r = &mut policy_fut => r,
_ = shutdown_signal() => { info!("received shutdown signal; shutting down"); Ok(()) }
}
}
#[cfg(unix)]
async fn shutdown_signal() {
use tokio::signal::unix::{signal, SignalKind};
match signal(SignalKind::terminate()) {
Ok(mut term) => {
tokio::select! {
_ = tokio::signal::ctrl_c() => {}
_ = term.recv() => {}
}
}
Err(_) => {
let _ = tokio::signal::ctrl_c().await;
}
}
}
#[cfg(windows)]
async fn shutdown_signal() {
use tokio::signal::windows;
let mut close = windows::ctrl_close().expect("install ctrl-close handler");
let mut shutdown = windows::ctrl_shutdown().expect("install ctrl-shutdown handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => {}
_ = close.recv() => {}
_ = shutdown.recv() => {}
}
}
fn propagate(which: &str, joined: Result<Result<()>, tokio::task::JoinError>) -> Result<()> {
match joined {
Ok(inner) => inner.with_context(|| format!("{which} loop failed")),
Err(join_err) => {
Err(anyhow::Error::new(join_err).context(format!("{which} task panicked/aborted")))
}
}
}
fn is_transient_udp_error(e: &std::io::Error) -> bool {
use std::io::ErrorKind;
#[cfg(unix)]
if e.raw_os_error() == Some(libc::ENOBUFS) {
return true;
}
#[cfg(windows)]
if e.raw_os_error() == Some(10055) {
return true;
}
matches!(
e.kind(),
ErrorKind::ConnectionRefused
| ErrorKind::ConnectionReset
| ErrorKind::ConnectionAborted
| ErrorKind::HostUnreachable
| ErrorKind::NetworkUnreachable
| ErrorKind::NetworkDown
| ErrorKind::AddrNotAvailable
| ErrorKind::Interrupted
)
}
async fn tun_to_net(
tun: Arc<TunDevice>,
socket: Arc<UdpSocket>,
cipher: Cipher,
master_key: Arc<[u8]>,
obfuscator: Option<Arc<Obfuscator>>,
) -> Result<()> {
let (tx, mut rx) = mpsc::channel::<Vec<u8>>(CHANNEL_DEPTH);
let reader = tokio::spawn(async move {
let mut buf = vec![0u8; MAX_IP_PACKET];
loop {
let n = tun
.recv(&mut buf)
.await
.context("failed to read from TUN device")?;
if n == 0 {
continue;
}
if tx.send(buf[..n].to_vec()).await.is_err() {
return Ok(());
}
}
});
let processor = tokio::spawn(async move {
let mut send_failures: u64 = 0;
while let Some(pkt) = rx.recv().await {
let n = pkt.len();
let datagram = match encrypt_packet(cipher, &master_key, &pkt) {
Ok(d) => d,
Err(e) => {
warn!("failed to encrypt a {n}-byte packet, dropping: {e}");
continue;
}
};
let wire = match obfuscator {
Some(ref o) => o.wrap(&datagram),
None => datagram,
};
if let Err(e) = socket.send(&wire).await {
if is_transient_udp_error(&e) {
send_failures += 1;
if send_failures == 1 {
warn!("transient send error, dropping packets until the path clears: {e}");
} else {
debug!("transient send error #{send_failures}: {e}");
}
continue;
}
return Err(e).context("failed to send datagram to server");
}
if send_failures > 0 {
info!("send path recovered after {send_failures} dropped packet(s)");
send_failures = 0;
}
debug!(
"tun->net: {n} bytes plaintext -> {} bytes on wire",
wire.len()
);
}
Ok(())
});
let mut reader = reader;
let mut processor = processor;
tokio::select! {
r = &mut reader => { processor.abort(); r.context("tun->net reader task panicked")? }
r = &mut processor => { reader.abort(); r.context("tun->net processor task panicked")? }
}
}
async fn net_to_tun(
tun: Arc<TunDevice>,
socket: Arc<UdpSocket>,
cipher: Cipher,
master_key: Arc<[u8]>,
obfuscator: Option<Arc<Obfuscator>>,
route_installer: Option<Arc<RouteInstaller>>,
) -> Result<()> {
let (tx, mut rx) = mpsc::channel::<Vec<u8>>(CHANNEL_DEPTH);
let reader = tokio::spawn(async move {
let mut buf = vec![0u8; max_datagram_size(cipher) + obfs::MAX_HEADER];
let mut recv_failures: u64 = 0;
loop {
let n = match socket.recv(&mut buf).await {
Ok(n) => n,
Err(e) if is_transient_udp_error(&e) => {
recv_failures += 1;
if recv_failures == 1 {
warn!("transient receive error, retrying until the path clears: {e}");
} else {
debug!("transient receive error #{recv_failures}: {e}");
}
tokio::time::sleep(TRANSIENT_RETRY_DELAY).await;
continue;
}
Err(e) => return Err(e).context("failed to receive datagram from server"),
};
if recv_failures > 0 {
info!("receive path recovered after {recv_failures} transient error(s)");
recv_failures = 0;
}
if tx.send(buf[..n].to_vec()).await.is_err() {
return Ok(());
}
}
});
let processor = tokio::spawn(async move {
while let Some(pkt) = rx.recv().await {
let n = pkt.len();
let decoded;
let datagram: &[u8] = match obfuscator {
Some(ref o) => match o.unwrap(&pkt) {
Some(inner) => {
decoded = inner;
&decoded
}
None => {
debug!("dropping {n}-byte non-obfs datagram");
continue;
}
},
None => &pkt,
};
let plaintext = match decrypt_packet(cipher, &master_key, datagram) {
Ok(p) => p,
Err(e) => {
debug!("dropping undecryptable {n}-byte datagram: {e}");
continue;
}
};
if mesh::is_control(&plaintext) {
match mesh::parse_control(&plaintext) {
Some(mesh::Control::RoutePush(push)) => match &route_installer {
Some(installer) => installer.apply(&push.routes),
None => {
debug!("ignoring route push: accept_routes is not enabled")
}
},
other => debug!(
"dropping {}-byte control payload ({other:?})",
plaintext.len()
),
}
continue;
}
if plaintext.len() < 20 {
debug!("dropping {}-byte sub-IP-header payload", plaintext.len());
continue;
}
tun.send(&plaintext)
.await
.context("failed to write packet to TUN device")?;
debug!(
"net->tun: {n} bytes datagram -> {} bytes plaintext",
plaintext.len()
);
}
Ok(())
});
let mut reader = reader;
let mut processor = processor;
tokio::select! {
r = &mut reader => { processor.abort(); r.context("net->tun reader task panicked")? }
r = &mut processor => { reader.abort(); r.context("net->tun processor task panicked")? }
}
}
async fn keepalive_loop(
socket: Arc<UdpSocket>,
cipher: Cipher,
master_key: Arc<[u8]>,
obfuscator: Option<Arc<Obfuscator>>,
interval: Duration,
payload: Vec<u8>,
) -> Result<()> {
let mut ticker = tokio::time::interval(interval);
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
loop {
ticker.tick().await;
let datagram = match encrypt_packet(cipher, &master_key, &payload) {
Ok(d) => d,
Err(e) => {
warn!("failed to encrypt keepalive, skipping: {e}");
continue;
}
};
let wire = match obfuscator {
Some(ref o) => o.wrap(&datagram),
None => datagram,
};
if let Err(e) = socket.send(&wire).await {
if is_transient_udp_error(&e) {
warn!("transient keepalive send error, retrying next tick: {e}");
continue;
}
return Err(e).context("failed to send keepalive to server");
}
debug!("sent {}-byte keepalive", wire.len());
}
}
fn print_routing_hint(tun: &TunConfig, server: &str) {
let peer = tun.peer_ip;
let local = tun.ip;
info!("-----------------------------------------------------------------");
info!("Tunnel is up (local {local}, peer {peer}). It does NOT change your");
info!("routing table. To send traffic through the tunnel, add routes by hand.");
info!("");
let server_host = server.rsplit_once(':').map(|(h, _)| h).unwrap_or(server);
let server_ip = server_host.parse::<Ipv4Addr>().ok();
#[cfg(target_os = "linux")]
{
info!("Linux:");
if let Some(ip) = server_ip {
info!(" # keep the server reachable over your real link (replace GW/DEV):");
info!(" sudo ip route add {ip}/32 via <YOUR_DEFAULT_GW> dev <YOUR_WAN_DEV>");
} else {
info!(" # first add a host route for the server's resolved IP via your real");
info!(" # gateway, so encrypted UDP does not re-enter the tunnel.");
}
info!(" # then route everything (or a subnet) through the tunnel peer:");
info!(" sudo ip route add 0.0.0.0/1 via {peer}");
info!(" sudo ip route add 128.0.0.0/1 via {peer}");
info!(" # (the two /1 routes override the default without deleting it)");
}
#[cfg(target_os = "macos")]
{
info!("macOS:");
if let Some(ip) = server_ip {
info!(" # keep the server reachable over your real link (replace GW):");
info!(" sudo route -n add -host {ip} <YOUR_DEFAULT_GW>");
} else {
info!(" # first add a host route for the server's resolved IP via your real");
info!(" # gateway, so encrypted UDP does not re-enter the tunnel.");
}
info!(" # then route everything through the tunnel peer:");
info!(" sudo route -n add -net 0.0.0.0/1 {peer}");
info!(" sudo route -n add -net 128.0.0.0/1 {peer}");
}
#[cfg(windows)]
{
info!("Windows (run in an elevated prompt):");
if let Some(ip) = server_ip {
info!(" :: keep the server reachable over your real link (replace GW):");
info!(" route add {ip} mask 255.255.255.255 <YOUR_DEFAULT_GW>");
} else {
info!(" :: first add a host route for the server's resolved IP via your real");
info!(" :: gateway, so encrypted UDP does not re-enter the tunnel.");
}
info!(" :: then route everything through the tunnel peer:");
info!(" route add 0.0.0.0 mask 128.0.0.0 {peer}");
info!(" route add 128.0.0.0 mask 128.0.0.0 {peer}");
}
#[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
{
let _ = server_ip;
info!("Add a host route to the server via your real gateway, then route the");
info!("desired destinations via the tunnel peer {peer}.");
}
info!("");
info!("To stop using the tunnel, delete the routes you added above.");
info!("-----------------------------------------------------------------");
if server_ip.is_none() {
warn!(
"server '{server}' is a hostname, not a literal IP: resolve it and add a \
host route for that IP via your real gateway before routing all traffic."
);
}
}