2023-05-09 21:28:07 +00:00
|
|
|
use crate::{
|
|
|
|
args,
|
|
|
|
utils,
|
|
|
|
};
|
2023-04-30 15:55:41 +00:00
|
|
|
use clap::Parser;
|
2023-05-09 21:28:07 +00:00
|
|
|
use log::{
|
|
|
|
debug,
|
|
|
|
info,
|
|
|
|
warn,
|
|
|
|
};
|
|
|
|
use serde::{
|
|
|
|
Deserialize,
|
|
|
|
Serialize,
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
env,
|
|
|
|
fs,
|
|
|
|
process::Command,
|
|
|
|
time::Duration,
|
|
|
|
};
|
2023-04-30 15:55:41 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
pub struct HttpProxyStatus {
|
2023-05-09 21:28:07 +00:00
|
|
|
pub open: bool,
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 19:58:21 +00:00
|
|
|
#[derive(Debug, Serialize, PartialEq)]
|
2023-04-30 15:55:41 +00:00
|
|
|
pub enum ProxyStatus {
|
|
|
|
Opening,
|
2023-05-09 21:28:07 +00:00
|
|
|
Open,
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ProxyStatus {
|
|
|
|
pub fn value(&self) -> String {
|
|
|
|
match *self {
|
|
|
|
ProxyStatus::Opening => String::from("opening\n"),
|
|
|
|
ProxyStatus::Open => String::from("open\n"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
struct Tunnel {
|
|
|
|
// http proxy tunnel wont have this field
|
|
|
|
dest: Option<String>,
|
|
|
|
port: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
struct Tunnels {
|
|
|
|
tunnels: Vec<Tunnel>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Tunnels {
|
|
|
|
fn default() -> Self {
|
2023-05-09 21:28:07 +00:00
|
|
|
Tunnels {
|
|
|
|
tunnels: Vec::new(),
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-17 20:30:07 +00:00
|
|
|
/// Looks for the `tunnels-config.json` at /home/$USER/.i2p-zero/config/
|
|
|
|
///
|
|
|
|
/// and attempts to extract the app and http proxy tunnel information.
|
2023-04-30 15:55:41 +00:00
|
|
|
async fn find_tunnels() {
|
|
|
|
let app_port = utils::get_app_port();
|
|
|
|
let file_path = format!(
|
|
|
|
"/home/{}/.i2p-zero/config/tunnels.json",
|
|
|
|
env::var("USER").unwrap_or(String::from("user"))
|
|
|
|
);
|
|
|
|
let contents = fs::read_to_string(file_path).unwrap_or(utils::empty_string());
|
|
|
|
debug!("i2p tunnels: {}", contents);
|
|
|
|
let has_app_tunnel = contents.contains(&format!("{}", app_port));
|
2023-05-17 19:58:21 +00:00
|
|
|
let proxy_port = get_i2p_proxy_port();
|
|
|
|
let has_http_tunnel = contents.contains(&proxy_port);
|
|
|
|
if !has_app_tunnel || !has_http_tunnel {
|
2023-04-30 15:55:41 +00:00
|
|
|
tokio::time::sleep(Duration::new(120, 0)).await;
|
2023-05-17 19:58:21 +00:00
|
|
|
}
|
|
|
|
if !has_app_tunnel {
|
|
|
|
debug!("creating app tunnel");
|
2023-04-30 15:55:41 +00:00
|
|
|
create_tunnel();
|
|
|
|
}
|
2023-05-17 19:58:21 +00:00
|
|
|
if !has_http_tunnel {
|
|
|
|
debug!("creating http tunnel");
|
|
|
|
create_http_proxy();
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 20:30:07 +00:00
|
|
|
/// Called on application startup for i2p tunnel creation,
|
|
|
|
///
|
|
|
|
/// proxy tunnel, etc. Logs proxy status every minute.
|
2023-04-30 15:55:41 +00:00
|
|
|
pub async fn start() {
|
|
|
|
info!("starting i2p-zero");
|
|
|
|
let args = args::Args::parse();
|
|
|
|
let path = args.i2p_zero_dir;
|
2023-05-17 19:58:21 +00:00
|
|
|
let output = Command::new(format!("{}/router/bin/i2p-zero", path)).spawn();
|
2023-05-17 11:39:24 +00:00
|
|
|
match output {
|
|
|
|
Ok(child) => debug!("{:?}", child.stdout),
|
2023-05-17 19:58:21 +00:00
|
|
|
_ => {
|
2023-05-17 11:39:24 +00:00
|
|
|
warn!("i2p-zero not installed, manual tunnel creation required");
|
|
|
|
()
|
2023-05-17 19:58:21 +00:00
|
|
|
}
|
2023-05-17 11:39:24 +00:00
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
find_tunnels().await;
|
2023-05-09 21:28:07 +00:00
|
|
|
{
|
|
|
|
tokio::spawn(async move {
|
2023-05-08 08:02:38 +00:00
|
|
|
let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(600000);
|
2023-05-08 06:21:52 +00:00
|
|
|
loop {
|
|
|
|
tick.recv().unwrap();
|
|
|
|
check_connection().await;
|
|
|
|
}
|
2023-05-09 21:28:07 +00:00
|
|
|
});
|
2023-05-08 06:21:52 +00:00
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 20:30:07 +00:00
|
|
|
/// Create an i2p tunnel for the NEVMES application
|
2023-04-30 15:55:41 +00:00
|
|
|
fn create_tunnel() {
|
|
|
|
info!("creating tunnel");
|
2023-04-30 16:17:32 +00:00
|
|
|
let args = args::Args::parse();
|
|
|
|
let path = args.i2p_zero_dir;
|
|
|
|
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
|
2023-05-09 21:28:07 +00:00
|
|
|
.args([
|
|
|
|
"server.create",
|
|
|
|
"127.0.0.1",
|
|
|
|
&format!("{}", utils::get_app_port()),
|
|
|
|
])
|
2023-04-30 15:55:41 +00:00
|
|
|
.spawn()
|
|
|
|
.expect("i2p-zero failed to create a tunnel");
|
|
|
|
debug!("{:?}", output.stdout);
|
|
|
|
}
|
|
|
|
|
2023-05-17 19:58:21 +00:00
|
|
|
/// Extract i2p port from command line arg
|
|
|
|
fn get_i2p_proxy_port() -> String {
|
|
|
|
let proxy_host = utils::get_i2p_http_proxy();
|
|
|
|
let values = proxy_host.split(":");
|
|
|
|
let mut v: Vec<String> = values.map(|s| String::from(s)).collect();
|
|
|
|
let port = v.remove(2);
|
|
|
|
port
|
|
|
|
}
|
2023-05-18 06:12:28 +00:00
|
|
|
|
2023-05-17 19:58:21 +00:00
|
|
|
/// Create the http proxy if it doesn't exist
|
|
|
|
fn create_http_proxy() {
|
|
|
|
let args = args::Args::parse();
|
|
|
|
let path = args.i2p_zero_dir;
|
|
|
|
info!("creating http proxy");
|
|
|
|
let port = get_i2p_proxy_port();
|
|
|
|
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
|
|
|
|
.args(["http.create", &port])
|
|
|
|
.spawn()
|
|
|
|
.expect("i2p-zero failed to create a http proxy");
|
|
|
|
debug!("{:?}", output.stdout);
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
|
2023-05-17 20:30:07 +00:00
|
|
|
/// This is the `dest` value of the app i2p tunnel
|
|
|
|
///
|
|
|
|
/// in `tunnels-config.json`.
|
2023-04-30 15:55:41 +00:00
|
|
|
pub fn get_destination() -> String {
|
|
|
|
let file_path = format!(
|
|
|
|
"/home/{}/.i2p-zero/config/tunnels.json",
|
|
|
|
env::var("USER").unwrap_or(String::from("user"))
|
|
|
|
);
|
2023-05-04 12:50:56 +00:00
|
|
|
// Don't panic if i2p-zero isn't installed
|
2023-05-09 21:28:07 +00:00
|
|
|
let contents = match fs::read_to_string(file_path) {
|
|
|
|
Ok(file) => file,
|
|
|
|
_ => utils::empty_string(),
|
|
|
|
};
|
2023-05-04 12:50:56 +00:00
|
|
|
if contents != utils::empty_string() {
|
|
|
|
let input = format!(r#"{contents}"#);
|
2023-05-17 11:39:24 +00:00
|
|
|
let j: Tunnels = serde_json::from_str(&input).unwrap_or(Default::default());
|
|
|
|
let mut destination: String = utils::empty_string();
|
|
|
|
let tunnels: Vec<Tunnel> = j.tunnels;
|
|
|
|
for tunnel in tunnels {
|
|
|
|
if tunnel.port == format!("{}", utils::get_app_port()) {
|
|
|
|
destination = tunnel.dest.unwrap_or(utils::empty_string());
|
|
|
|
}
|
|
|
|
}
|
2023-05-09 21:28:07 +00:00
|
|
|
return destination;
|
2023-05-04 12:50:56 +00:00
|
|
|
}
|
|
|
|
utils::empty_string()
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
|
2023-05-17 20:30:07 +00:00
|
|
|
/// Ping the i2p-zero http proxy `tunnel-control http.state <port>`
|
2023-05-17 19:58:21 +00:00
|
|
|
pub async fn check_connection() -> ProxyStatus {
|
|
|
|
let args = args::Args::parse();
|
|
|
|
let path = args.i2p_zero_dir;
|
|
|
|
let port = get_i2p_proxy_port();
|
|
|
|
let output = Command::new(format!("{}/router/bin/tunnel-control.sh", path))
|
|
|
|
.args(["http.state", &port])
|
|
|
|
.output()
|
|
|
|
.expect("check i2p connection failed");
|
|
|
|
let str_status = String::from_utf8(output.stdout).unwrap();
|
|
|
|
if str_status == ProxyStatus::Open.value() {
|
2023-05-19 00:13:07 +00:00
|
|
|
debug!("http proxy is open");
|
2023-05-17 19:58:21 +00:00
|
|
|
ProxyStatus::Open
|
|
|
|
} else {
|
2023-05-22 17:46:28 +00:00
|
|
|
debug!("http proxy is opening");
|
2023-05-17 19:58:21 +00:00
|
|
|
ProxyStatus::Opening
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|