mirror of
https://github.com/dndx/phantun.git
synced 2025-09-16 04:04:29 +08:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
726ecac9cf | ||
|
2ef0a056be | ||
|
cb9dd3e931 | ||
|
7db7164193 | ||
|
def134d73b | ||
|
b3c781cdc5 | ||
|
d5e30c113f | ||
|
e2a9194f6f | ||
|
d0eaefe5d0 | ||
|
299646a54f | ||
|
8b28cdc6c2 | ||
|
a8ad203754 | ||
|
33e510e7ba | ||
|
521a3f1a01 | ||
|
c5a5116808 | ||
|
e8f2457cb5 | ||
|
583cdbe300 | ||
|
91988520e5 | ||
|
49cc6a6865 | ||
|
7390d4bf27 | ||
|
95e762f5fd | ||
|
c9043015f2 | ||
|
494abf37c5 | ||
|
cab87bd75b | ||
|
042f5af49f | ||
|
f667f56747 | ||
|
49665b906f | ||
|
e9cde27923 |
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
3
.github/workflows/rust.yml
vendored
3
.github/workflows/rust.yml
vendored
@@ -12,6 +12,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Run lint
|
||||
run: cargo clippy --verbose
|
||||
- name: Build
|
||||
|
30
README.md
30
README.md
@@ -31,16 +31,16 @@ Table of Contents
|
||||
|
||||
# Latest release
|
||||
|
||||
[v0.2.1](https://github.com/dndx/phantun/releases/tag/v0.2.1)
|
||||
[v0.2.4](https://github.com/dndx/phantun/releases/tag/v0.2.4)
|
||||
|
||||
# Overview
|
||||
|
||||
Phanton is a project that obfuscated UDP packets into TCP connections. It aims to
|
||||
Phantun is a project that obfuscated UDP packets into TCP connections. It aims to
|
||||
achieve maximum performance with minimum processing and encapsulation overhead.
|
||||
|
||||
It is commonly used in environments where UDP is blocked/throttled but TCP is allowed through.
|
||||
|
||||
Phanton simply converts a stream of UDP packets into obfuscated TCP stream packets. The TCP stack
|
||||
Phantun simply converts a stream of UDP packets into obfuscated TCP stream packets. The TCP stack
|
||||
used by Phantun is designed to pass through most L3/L4 stateful/stateless firewalls/NAT
|
||||
devices. It will **not** be able to pass through L7 proxies.
|
||||
However, the advantage of this approach is that none of the common UDP over TCP performance killer
|
||||
@@ -84,6 +84,10 @@ and change the destination IP address to where the server is listening for incom
|
||||
In those cases, the machine/iptables running Phantun acts as the "router" that allows Phantun
|
||||
to communicate with outside using it's private IP addresses.
|
||||
|
||||
As of Phantun v0.2.2, IPv6 support for UDP endpoints has been added, however Fake TCP IPv6 support
|
||||
has not been finished yet. To specify an IPv6 address, use the following format: `[::1]:1234` with
|
||||
the command line options.
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
## 1. Enable Kernel IP forwarding
|
||||
@@ -130,7 +134,7 @@ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
|
||||
Server needs to DNAT the TCP listening port to Phantun's TUN interface address.
|
||||
|
||||
Note: change `eth0` to whatever actual physical interface name is and `4567` to
|
||||
actual TCP port number used by Phanton server
|
||||
actual TCP port number used by Phantun server
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
@@ -181,6 +185,12 @@ rule specified above. `127.0.0.1:1234` is the UDP Server to connect to for new c
|
||||
RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote 127.0.0.1:1234
|
||||
```
|
||||
|
||||
Or use host name with `--remote`:
|
||||
|
||||
```
|
||||
RUST_LOG=info /usr/local/bin/phantun_server --local 4567 --remote example.com:1234
|
||||
```
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
### Client
|
||||
@@ -192,6 +202,12 @@ the Phantun Server to connect.
|
||||
RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote 10.0.0.1:4567
|
||||
```
|
||||
|
||||
Or use host name with `--remote`:
|
||||
|
||||
```
|
||||
RUST_LOG=info /usr/local/bin/phantun_client --local 127.0.0.1:1234 --remote example.com:4567
|
||||
```
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
# MTU overhead
|
||||
@@ -250,7 +266,7 @@ for tunneling TCP/UDP traffic between two test instances and MTU has been tuned
|
||||
|
||||
# Future plans
|
||||
|
||||
* IPv6 support
|
||||
* IPv6 support for fake-tcp
|
||||
* Load balancing a single UDP stream into multiple TCP streams
|
||||
* Integration tests
|
||||
* Auto insertion/removal of required firewall rules
|
||||
@@ -260,7 +276,7 @@ for tunneling TCP/UDP traffic between two test instances and MTU has been tuned
|
||||
# Compariation to udp2raw
|
||||
[udp2raw](https://github.com/wangyu-/udp2raw-tunnel) is another popular project by [@wangyu-](https://github.com/wangyu-)
|
||||
that is very similar to what Phantun can do. In fact I took inspirations of Phantun from udp2raw. The biggest reason for
|
||||
developing Phanton is because of lack of performance when running udp2raw (especially on multi-core systems such as Raspberry Pi).
|
||||
developing Phantun is because of lack of performance when running udp2raw (especially on multi-core systems such as Raspberry Pi).
|
||||
However, the goal is never to be as feature complete as udp2raw and only support the most common use cases. Most notably, UDP over ICMP
|
||||
and UDP over UDP mode are not supported and there is no anti-replay nor encryption support. The benefit of this is much better
|
||||
performance overall and less MTU overhead because lack of additional headers inside the TCP payload.
|
||||
@@ -278,7 +294,7 @@ Here is a quick overview of comparison between those two to help you choose:
|
||||
| Tunneling MTU overhead | 12 bytes | 44 bytes |
|
||||
| Seprate TCP connections for each UDP connection | Client/Server | Server only |
|
||||
| Anti-replay, encryption | ❌ | ✅ |
|
||||
| IPv6 | Planned | ✅ |
|
||||
| IPv6 | UDP only | ✅ |
|
||||
|
||||
[Back to TOC](#table-of-contents)
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "fake-tcp"
|
||||
version = "0.1.2"
|
||||
edition = "2018"
|
||||
version = "0.2.3"
|
||||
edition = "2021"
|
||||
authors = ["Datong Sun <dndx@idndx.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/dndx/phantun"
|
||||
@@ -16,9 +16,9 @@ benchmark = []
|
||||
|
||||
[dependencies]
|
||||
bytes = "1"
|
||||
pnet = "0.28.0"
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
pnet = "0.28"
|
||||
tokio = { version = "1.14", features = ["full"] }
|
||||
rand = { version = "0.8", features = ["small_rng"] }
|
||||
log = "0.4"
|
||||
internet-checksum = "0.2.0"
|
||||
dndx-fork-tokio-tun = "0.3.16"
|
||||
internet-checksum = "0.2"
|
||||
tokio-tun = "0.5"
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#![cfg_attr(feature = "benchmark", feature(test))]
|
||||
|
||||
pub mod packet;
|
||||
extern crate dndx_fork_tokio_tun as tokio_tun;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use log::{error, info, trace, warn};
|
||||
@@ -15,7 +14,6 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
use tokio::sync::watch;
|
||||
use tokio::sync::Mutex as AsyncMutex;
|
||||
use tokio::time;
|
||||
use tokio_tun::Tun;
|
||||
@@ -69,8 +67,6 @@ pub struct Socket {
|
||||
seq: AtomicU32,
|
||||
ack: AtomicU32,
|
||||
state: State,
|
||||
closing_tx: watch::Sender<()>,
|
||||
closing_rx: watch::Receiver<()>,
|
||||
}
|
||||
|
||||
impl Socket {
|
||||
@@ -83,7 +79,6 @@ impl Socket {
|
||||
state: State,
|
||||
) -> (Socket, Sender<Bytes>) {
|
||||
let (incoming_tx, incoming_rx) = mpsc::channel(MPSC_BUFFER_LEN);
|
||||
let (closing_tx, closing_rx) = watch::channel(());
|
||||
|
||||
(
|
||||
Socket {
|
||||
@@ -95,8 +90,6 @@ impl Socket {
|
||||
seq: AtomicU32::new(0),
|
||||
ack: AtomicU32::new(ack.unwrap_or(0)),
|
||||
state,
|
||||
closing_tx,
|
||||
closing_rx,
|
||||
},
|
||||
incoming_tx,
|
||||
)
|
||||
@@ -114,8 +107,6 @@ impl Socket {
|
||||
}
|
||||
|
||||
pub async fn send(&self, payload: &[u8]) -> Option<()> {
|
||||
let mut closing = self.closing_rx.clone();
|
||||
|
||||
match self.state {
|
||||
State::Established => {
|
||||
let buf = self.build_tcp_packet(tcp::TcpFlags::ACK, Some(payload));
|
||||
@@ -123,12 +114,8 @@ impl Socket {
|
||||
|
||||
tokio::select! {
|
||||
res = self.tun.send(&buf) => {
|
||||
res.unwrap();
|
||||
Some(())
|
||||
res.ok().and(Some(()))
|
||||
},
|
||||
_ = closing.changed() => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -136,43 +123,31 @@ impl Socket {
|
||||
}
|
||||
|
||||
pub async fn recv(&self, buf: &mut [u8]) -> Option<usize> {
|
||||
let mut closing = self.closing_rx.clone();
|
||||
|
||||
match self.state {
|
||||
State::Established => {
|
||||
let mut incoming = self.incoming.lock().await;
|
||||
tokio::select! {
|
||||
Some(raw_buf) = incoming.recv() => {
|
||||
let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf);
|
||||
incoming.recv().await.and_then(|raw_buf| {
|
||||
let (_v4_packet, tcp_packet) = parse_ipv4_packet(&raw_buf);
|
||||
|
||||
if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 {
|
||||
info!("Connection {} reset by peer", self);
|
||||
self.close();
|
||||
return None;
|
||||
}
|
||||
|
||||
let payload = tcp_packet.payload();
|
||||
|
||||
self.ack
|
||||
.store(tcp_packet.get_sequence().wrapping_add(1), Ordering::Relaxed);
|
||||
|
||||
buf[..payload.len()].copy_from_slice(payload);
|
||||
|
||||
Some(payload.len())
|
||||
},
|
||||
_ = closing.changed() => {
|
||||
None
|
||||
if (tcp_packet.get_flags() & tcp::TcpFlags::RST) != 0 {
|
||||
info!("Connection {} reset by peer", self);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let payload = tcp_packet.payload();
|
||||
|
||||
self.ack
|
||||
.store(tcp_packet.get_sequence().wrapping_add(1), Ordering::Relaxed);
|
||||
|
||||
buf[..payload.len()].copy_from_slice(payload);
|
||||
|
||||
Some(payload.len())
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.closing_tx.send(()).unwrap();
|
||||
}
|
||||
|
||||
async fn accept(mut self) {
|
||||
for _ in 0..RETRIES {
|
||||
match self.state {
|
||||
@@ -282,7 +257,7 @@ impl Drop for Socket {
|
||||
if let Err(e) = self.tun.try_send(&buf) {
|
||||
warn!("Unable to send RST to remote end: {}", e);
|
||||
}
|
||||
self.close();
|
||||
|
||||
info!("Fake TCP connection to {} closed", self);
|
||||
}
|
||||
}
|
||||
@@ -396,11 +371,10 @@ impl Stack {
|
||||
|
||||
} else {
|
||||
trace!("Cache miss, checking the shared tuples table for connection");
|
||||
let sender;
|
||||
{
|
||||
let sender = {
|
||||
let tuples = shared.tuples.read().unwrap();
|
||||
sender = tuples.get(&tuple).cloned();
|
||||
}
|
||||
tuples.get(&tuple).cloned()
|
||||
};
|
||||
|
||||
if let Some(c) = sender {
|
||||
trace!("Storing connection information into local tuples");
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "phantun"
|
||||
version = "0.2.1"
|
||||
edition = "2018"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
authors = ["Datong Sun <dndx@idndx.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/dndx/phantun"
|
||||
@@ -11,11 +11,11 @@ Transforms UDP stream into (fake) TCP streams that can go through
|
||||
Layer 3 & Layer 4 (NAPT) firewalls/NATs.
|
||||
"""
|
||||
[dependencies]
|
||||
clap = "2.33.3"
|
||||
socket2 = { version = "0.4.2", features = ["all"] }
|
||||
fake-tcp = "0.1.2"
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
clap = { version = "3.0", features = ["cargo"] }
|
||||
socket2 = { version = "0.4", features = ["all"] }
|
||||
fake-tcp = "0.2"
|
||||
tokio = { version = "1.14", features = ["full"] }
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4.0"
|
||||
dndx-fork-tokio-tun = "0.3.16"
|
||||
num_cpus = "1.13.0"
|
||||
pretty_env_logger = "0.4"
|
||||
tokio-tun = "0.5"
|
||||
num_cpus = "1.13"
|
||||
|
@@ -1,12 +1,10 @@
|
||||
extern crate dndx_fork_tokio_tun as tokio_tun;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use fake_tcp::packet::MAX_PACKET_LEN;
|
||||
use fake_tcp::{Socket, Stack};
|
||||
use log::{debug, error, info};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::net::UdpSocket;
|
||||
@@ -16,8 +14,17 @@ use tokio_tun::TunBuilder;
|
||||
|
||||
const UDP_TTL: Duration = Duration::from_secs(180);
|
||||
|
||||
fn new_udp_reuseport(addr: SocketAddrV4) -> UdpSocket {
|
||||
let udp_sock = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::DGRAM, None).unwrap();
|
||||
fn new_udp_reuseport(addr: SocketAddr) -> UdpSocket {
|
||||
let udp_sock = socket2::Socket::new(
|
||||
if addr.is_ipv4() {
|
||||
socket2::Domain::IPV4
|
||||
} else {
|
||||
socket2::Domain::IPV6
|
||||
},
|
||||
socket2::Type::DGRAM,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
udp_sock.set_reuse_port(true).unwrap();
|
||||
// from tokio-rs/mio/blob/master/src/sys/unix/net.rs
|
||||
udp_sock.set_cloexec(true).unwrap();
|
||||
@@ -35,25 +42,25 @@ async fn main() {
|
||||
.version(crate_version!())
|
||||
.author("Datong Sun (github.com/dndx)")
|
||||
.arg(
|
||||
Arg::with_name("local")
|
||||
.short("l")
|
||||
Arg::new("local")
|
||||
.short('l')
|
||||
.long("local")
|
||||
.required(true)
|
||||
.value_name("IP:PORT")
|
||||
.help("Sets the IP and port where Phantun Client listens for incoming UDP datagrams")
|
||||
.help("Sets the IP and port where Phantun Client listens for incoming UDP datagrams, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("remote")
|
||||
.short("r")
|
||||
Arg::new("remote")
|
||||
.short('r')
|
||||
.long("remote")
|
||||
.required(true)
|
||||
.value_name("IP:PORT")
|
||||
.help("Sets the address and port where Phantun Client connects to Phantun Server")
|
||||
.value_name("IP or HOST NAME:PORT")
|
||||
.help("Sets the address or host name and port where Phantun Client connects to Phantun Server")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun")
|
||||
Arg::new("tun")
|
||||
.long("tun")
|
||||
.required(false)
|
||||
.value_name("tunX")
|
||||
@@ -62,7 +69,7 @@ async fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun_local")
|
||||
Arg::new("tun_local")
|
||||
.long("tun-local")
|
||||
.required(false)
|
||||
.value_name("IP")
|
||||
@@ -71,7 +78,7 @@ async fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun_peer")
|
||||
Arg::new("tun_peer")
|
||||
.long("tun-peer")
|
||||
.required(false)
|
||||
.value_name("IP")
|
||||
@@ -83,16 +90,24 @@ async fn main() {
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let local_addr: SocketAddrV4 = matches
|
||||
let local_addr: SocketAddr = matches
|
||||
.value_of("local")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("bad local address");
|
||||
let remote_addr: SocketAddrV4 = matches
|
||||
.value_of("remote")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("bad remote address");
|
||||
|
||||
let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
|
||||
.await
|
||||
.expect("bad remote address or host")
|
||||
.find(|addr| addr.is_ipv4())
|
||||
.expect("unable to resolve remote host name or no valid A record was returned");
|
||||
let remote_addr = if let SocketAddr::V4(addr) = remote_addr {
|
||||
addr
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
info!("Remote address is: {}", remote_addr);
|
||||
|
||||
let tun_local: Ipv4Addr = matches
|
||||
.value_of("tun_local")
|
||||
.unwrap()
|
||||
@@ -117,7 +132,7 @@ async fn main() {
|
||||
info!("Created TUN device {}", tun[0].name());
|
||||
|
||||
let udp_sock = Arc::new(new_udp_reuseport(local_addr));
|
||||
let connections = Arc::new(RwLock::new(HashMap::<SocketAddrV4, Arc<Socket>>::new()));
|
||||
let connections = Arc::new(RwLock::new(HashMap::<SocketAddr, Arc<Socket>>::new()));
|
||||
|
||||
let mut stack = Stack::new(tun);
|
||||
|
||||
@@ -126,7 +141,7 @@ async fn main() {
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Ok((size, SocketAddr::V4(addr))) = udp_sock.recv_from(&mut buf_r) => {
|
||||
Ok((size, addr)) = udp_sock.recv_from(&mut buf_r) => {
|
||||
// seen UDP packet to listening socket, this means:
|
||||
// 1. It is a new UDP connection, or
|
||||
// 2. It is some extra packets not filtered by more specific
|
||||
|
@@ -1,10 +1,8 @@
|
||||
extern crate dndx_fork_tokio_tun as tokio_tun;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use fake_tcp::packet::MAX_PACKET_LEN;
|
||||
use fake_tcp::Stack;
|
||||
use log::{error, info};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::net::Ipv4Addr;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::time::{self, Duration};
|
||||
use tokio_tun::TunBuilder;
|
||||
@@ -18,8 +16,8 @@ async fn main() {
|
||||
.version(crate_version!())
|
||||
.author("Datong Sun (github.com/dndx)")
|
||||
.arg(
|
||||
Arg::with_name("local")
|
||||
.short("l")
|
||||
Arg::new("local")
|
||||
.short('l')
|
||||
.long("local")
|
||||
.required(true)
|
||||
.value_name("PORT")
|
||||
@@ -27,16 +25,16 @@ async fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("remote")
|
||||
.short("r")
|
||||
Arg::new("remote")
|
||||
.short('r')
|
||||
.long("remote")
|
||||
.required(true)
|
||||
.value_name("IP:PORT")
|
||||
.help("Sets the address and port where Phantun Server forwards UDP packets to")
|
||||
.value_name("IP or HOST NAME:PORT")
|
||||
.help("Sets the address or host name and port where Phantun Server forwards UDP packets to, IPv6 address need to be specified as: \"[IPv6]:PORT\"")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun")
|
||||
Arg::new("tun")
|
||||
.long("tun")
|
||||
.required(false)
|
||||
.value_name("tunX")
|
||||
@@ -45,7 +43,7 @@ async fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun_local")
|
||||
Arg::new("tun_local")
|
||||
.long("tun-local")
|
||||
.required(false)
|
||||
.value_name("IP")
|
||||
@@ -54,7 +52,7 @@ async fn main() {
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("tun_peer")
|
||||
Arg::new("tun_peer")
|
||||
.long("tun-peer")
|
||||
.required(false)
|
||||
.value_name("IP")
|
||||
@@ -71,11 +69,14 @@ async fn main() {
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("bad local port");
|
||||
let remote_addr: SocketAddrV4 = matches
|
||||
.value_of("remote")
|
||||
.unwrap()
|
||||
.parse()
|
||||
.expect("bad remote address");
|
||||
|
||||
let remote_addr = tokio::net::lookup_host(matches.value_of("remote").unwrap())
|
||||
.await
|
||||
.expect("bad remote address or host")
|
||||
.next()
|
||||
.expect("unable to resolve remote host name");
|
||||
info!("Remote address is: {}", remote_addr);
|
||||
|
||||
let tun_local: Ipv4Addr = matches
|
||||
.value_of("tun_local")
|
||||
.unwrap()
|
||||
@@ -113,7 +114,13 @@ async fn main() {
|
||||
info!("New connection: {}", sock);
|
||||
|
||||
tokio::spawn(async move {
|
||||
let udp_sock = UdpSocket::bind("0.0.0.0:0").await.unwrap();
|
||||
let udp_sock = UdpSocket::bind(if remote_addr.is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
udp_sock.connect(remote_addr).await.unwrap();
|
||||
|
||||
loop {
|
||||
|
Reference in New Issue
Block a user