-
Notifications
You must be signed in to change notification settings - Fork 342
/
connect.rs
98 lines (78 loc) · 2.68 KB
/
connect.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::{sync::Arc, time::Duration};
use clone_macro::clone;
use futures_util::{future::select_all, FutureExt, TryFutureExt};
use itertools::Itertools;
use smol::prelude::*;
use smolscale::immortal::{Immortal, RespawnStrategy};
use crate::{config::ConnectOpt, connect::tunnel::ClientTunnel};
use crate::debugpack::DebugPack;
mod dns;
mod port_forwarder;
mod socks5;
mod stats;
mod tunnel;
mod vpn;
pub struct ConnectDaemon {
ctx: ConnectContext,
_task: Immortal,
}
impl ConnectDaemon {
/// Starts a new ConnectClient. If initialization fails, returns an error.
pub async fn start(opt: ConnectOpt) -> anyhow::Result<Self> {
log::info!(
"connect mode starting: exit = {:?}, use_bridges = {}",
opt.exit_server,
opt.use_bridges
);
let tunnel = ClientTunnel::new(opt.clone()).into();
let ctx = ConnectContext {
tunnel,
opt: opt.clone(),
debug: Arc::new(DebugPack::new(&opt.common.debugpack_path)?),
};
Ok(Self {
ctx: ctx.clone(),
_task: Immortal::respawn(
RespawnStrategy::JitterDelay(Duration::from_secs(1), Duration::from_secs(5)),
clone!([ctx], move || connect_loop(ctx.clone())
.map_err(|e| log::error!("connect_loop restart: {:?}", e))),
),
})
}
/// Gets a handle to the debug pack from the outside.
pub fn debug(&self) -> &DebugPack {
&self.ctx.debug
}
}
#[derive(Clone)]
pub struct ConnectContext {
opt: ConnectOpt,
tunnel: Arc<ClientTunnel>,
debug: Arc<DebugPack>,
}
async fn connect_loop(ctx: ConnectContext) -> anyhow::Result<()> {
let socks2http = smolscale::spawn(crate::socks2http::run_tokio(ctx.opt.http_listen, {
let mut addr = ctx.opt.socks5_listen;
addr.set_ip("127.0.0.1".parse().unwrap());
addr
}));
let socks5 = smolscale::spawn(socks5::socks5_loop(ctx.clone()));
let dns = smolscale::spawn(dns::dns_loop(ctx.clone(), ctx.opt.dns_listen));
let forward_ports = ctx
.opt
.forward_ports
.iter()
.map(|v| smolscale::spawn(port_forwarder::port_forwarder(ctx.clone(), v.clone())))
.chain(std::iter::once(smolscale::spawn(smol::future::pending()))) // ensures there's at least one
.collect_vec();
let vpn = smolscale::spawn(vpn::vpn_loop(ctx.clone()));
let stats = smolscale::spawn(stats::serve_stats_loop(ctx.clone()));
socks2http
.race(socks5)
.race(dns)
.race(select_all(forward_ports).map(|s| s.0))
.race(vpn)
.race(stats)
.await?;
anyhow::bail!("somehow ran off the edge of a cliff")
}