use bytes::Bytes;
use derivative::Derivative;
use geph5_broker_protocol::Credential;
use geph5_client::{BridgeMode, BrokerSource, Config};
use geph_nat::GephNat;
use parking_lot::RwLock;
use sillad::Pipe;
use smol::{
channel::{Receiver, Sender},
Task,
};
use smol_str::SmolStr;
use std::{
net::SocketAddr,
time::{Duration, SystemTime},
};
use sosistab2::Stream;
use std::sync::Arc;
use std::net::Ipv4Addr;
use crate::config::ConnectOpt;
use super::stats::{gatherer::StatItem, STATS_GATHERER};
#[derive(Clone)]
pub struct BinderTunnelParams {
pub exit_server: Option<String>,
pub use_bridges: bool,
pub force_bridge: Option<Ipv4Addr>,
pub force_protocol: Option<String>,
}
#[derive(Clone)]
struct TunnelCtx {
recv_socks5_conn: Receiver<(String, Sender<Stream>)>,
connect_status: Arc<RwLock<ConnectionStatus>>,
recv_vpn_outgoing: Receiver<Bytes>,
send_vpn_incoming: Sender<Bytes>,
}
#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub enum ConnectionStatus {
Connecting,
Connected { protocol: SmolStr, address: SmolStr },
}
impl ConnectionStatus {
pub fn connected(&self) -> bool {
matches!(self, Self::Connected { .. })
}
}
pub struct ClientTunnel {
client: geph5_client::Client,
_stat_reporter: Task<()>,
}
impl ClientTunnel {
pub fn new(opt: ConnectOpt) -> Self {
let (username, password) = match opt.auth.auth_kind {
Some(crate::config::AuthKind::AuthPassword { username, password }) => {
(username, password)
}
_ => todo!(),
};
let client = geph5_client::Client::start(Config {
socks5_listen: None,
http_proxy_listen: None,
control_listen: None,
exit_constraint: geph5_client::ExitConstraint::Auto,
bridge_mode: if opt.use_bridges {
BridgeMode::ForceBridges
} else {
BridgeMode::Auto
},
cache: None,
broker: Some(BrokerSource::Fronted {
front: "https://vuejs.org".into(),
host: "svitania-naidallszei-2.netlify.app".into(),
}),
vpn: false,
spoof_dns: true,
passthrough_china: false,
dry_run: false,
credentials: Credential::LegacyUsernamePassword { username, password },
});
let handle = client.control_client();
let stat_reporter = smolscale::spawn(async move {
loop {
smol::Timer::after(Duration::from_secs(1)).await;
let info = handle.conn_info().await.unwrap();
let recv_bytes = handle.stat_num("total_rx_bytes".into()).await.unwrap();
let send_bytes = handle.stat_num("total_tx_bytes".into()).await.unwrap();
match info {
geph5_client::ConnInfo::Connecting => {}
geph5_client::ConnInfo::Connected(conn) => STATS_GATHERER.push(StatItem {
time: SystemTime::now(),
endpoint: conn.bridge.into(),
protocol: conn.protocol.into(),
ping: Duration::from_millis(100),
send_bytes: send_bytes as u64,
recv_bytes: recv_bytes as u64,
}),
}
}
});
Self {
client,
_stat_reporter: stat_reporter,
}
}
pub async fn status(&self) -> ConnectionStatus {
let conn_info = self.client.control_client().conn_info().await.unwrap();
match conn_info {
geph5_client::ConnInfo::Connecting => ConnectionStatus::Connecting,
geph5_client::ConnInfo::Connected(info) => ConnectionStatus::Connected {
protocol: info.protocol.into(),
address: info.bridge.into(),
},
}
}
pub async fn connect_stream(&self, remote: &str) -> anyhow::Result<Box<dyn Pipe>> {
self.client.open_conn(remote).await
}
pub async fn send_vpn(&self, msg: &[u8]) -> anyhow::Result<()> {
self.client.send_vpn_packet(msg.to_vec().into()).await
}
pub async fn recv_vpn(&self) -> anyhow::Result<Bytes> {
self.client.recv_vpn_packet().await
}
}