122 lines
4.6 KiB
Rust
122 lines
4.6 KiB
Rust
//! Local test server credentials only. Never accepts a real subscription on argv.
|
|
use minivless::{
|
|
config, guardian,
|
|
storage::Preferences,
|
|
tunnel::{AppState, Phase},
|
|
vless,
|
|
};
|
|
use std::{
|
|
io::{Read, Write},
|
|
net::{SocketAddr, TcpStream, UdpSocket},
|
|
path::{Path, PathBuf},
|
|
time::{Duration, Instant},
|
|
};
|
|
fn main() {
|
|
let a: Vec<String> = std::env::args().collect();
|
|
if a.get(1).map(String::as_str) == Some("--core-guardian") {
|
|
std::process::exit(guardian::run(Path::new(&a[2]), Path::new(&a[3])));
|
|
}
|
|
if a.get(1).map(String::as_str) == Some("--fetch") {
|
|
let mut s = TcpStream::connect_timeout(
|
|
&a[2].parse::<SocketAddr>().unwrap(),
|
|
Duration::from_secs(4),
|
|
)
|
|
.unwrap();
|
|
s.set_read_timeout(Some(Duration::from_secs(4))).unwrap();
|
|
s.write_all(b"GET / HTTP/1.0\r\nHost: test\r\n\r\n")
|
|
.unwrap();
|
|
let mut text = String::new();
|
|
s.read_to_string(&mut text).unwrap();
|
|
println!("{}", text.split("\r\n\r\n").nth(1).unwrap());
|
|
return;
|
|
}
|
|
if a.get(1).map(String::as_str) == Some("--dns") {
|
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
|
socket
|
|
.set_read_timeout(Some(Duration::from_secs(5)))
|
|
.unwrap();
|
|
let query = b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04test\x07example\x00\x00\x01\x00\x01";
|
|
socket
|
|
.send_to(query, a.get(2).map(String::as_str).unwrap_or("8.8.8.8:53"))
|
|
.unwrap();
|
|
let mut reply = [0; 512];
|
|
let (n, _) = socket.recv_from(&mut reply).unwrap();
|
|
assert!(n >= 4);
|
|
println!(
|
|
"{}.{}.{}.{}",
|
|
reply[n - 4],
|
|
reply[n - 3],
|
|
reply[n - 2],
|
|
reply[n - 1]
|
|
);
|
|
return;
|
|
}
|
|
if a.get(1).map(String::as_str) == Some("--udp") {
|
|
let addr = a[2].parse::<SocketAddr>().unwrap();
|
|
let socket = UdpSocket::bind(if addr.is_ipv6() {
|
|
"[::]:0"
|
|
} else {
|
|
"0.0.0.0:0"
|
|
})
|
|
.unwrap();
|
|
socket
|
|
.set_read_timeout(Some(Duration::from_secs(4)))
|
|
.unwrap();
|
|
socket.send_to(b"peer", addr).unwrap();
|
|
let mut b = [0; 512];
|
|
let (n, _) = socket.recv_from(&mut b).unwrap();
|
|
println!("{}", String::from_utf8_lossy(&b[..n]));
|
|
return;
|
|
}
|
|
if a.get(1).map(String::as_str) == Some("--emit-configs") {
|
|
let dir = PathBuf::from(&a[2]);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
for (name,q) in [("plain",""),("tls","?security=tls&sni=example.com"),("reality","?security=reality&sni=example.com&pbk=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sid=0123&fp=chrome&flow=xtls-rprx-vision"),("ws","?type=ws&security=tls&path=%2Fsocket&host=example.com")] {
|
|
let v=vless::parse(&format!("vless://b831381d-6324-4d53-ad4f-8cda48b30811@192.0.2.2:2443{q}")).unwrap();
|
|
for split in [true,false] {let c=config::generate(&v,&["/usr/bin/curl".into()],split,1000);std::fs::write(dir.join(format!("{name}-{split}.json")),serde_json::to_vec_pretty(&c).unwrap()).unwrap();}
|
|
}
|
|
return;
|
|
}
|
|
assert_eq!(a.get(1).map(String::as_str), Some("--connect"));
|
|
let state = AppState::new(
|
|
a[3].clone().into(),
|
|
Some(a[2].clone().into()),
|
|
std::env::current_exe().unwrap(),
|
|
);
|
|
let request = Preferences {
|
|
// An ignored query value must not interfere with internal version parsing
|
|
// even though that value is redacted from user-visible core logs.
|
|
vless_url: "vless://b831381d-6324-4d53-ad4f-8cda48b30811@192.0.2.2:2443?remark=1.14".into(),
|
|
applications: vec![a[4].clone()],
|
|
split_tunneling: a[5] == "split",
|
|
..Default::default()
|
|
};
|
|
state.connect(request).unwrap();
|
|
let until = Instant::now() + Duration::from_secs(20);
|
|
while Instant::now() < until {
|
|
let status = state.status();
|
|
if status.phase == Phase::Connected {
|
|
println!("READY");
|
|
std::io::stdout().flush().unwrap();
|
|
break;
|
|
}
|
|
if status.phase == Phase::Error {
|
|
eprintln!(
|
|
"{}\n{}",
|
|
status.error.unwrap_or_default(),
|
|
state.logs().join("\n")
|
|
);
|
|
std::process::exit(1);
|
|
}
|
|
std::thread::sleep(Duration::from_millis(50));
|
|
}
|
|
if state.status().phase != Phase::Connected {
|
|
state.shutdown();
|
|
panic!("startup timeout");
|
|
}
|
|
let mut b = [0; 1];
|
|
let _ = std::io::stdin().read(&mut b);
|
|
state.shutdown();
|
|
println!("STOPPED");
|
|
}
|