tor: add service key result check

This commit is contained in:
ardocrat 2024-05-18 14:55:01 +03:00
parent eec434ecaa
commit c4fa0f7ec6

View file

@ -80,9 +80,9 @@ impl Default for Tor {
let runtime = TokioNativeTlsRuntime::create().unwrap(); let runtime = TokioNativeTlsRuntime::create().unwrap();
let config = Self::build_config(); let config = Self::build_config();
let client = TorClient::with_runtime(runtime) let client = TorClient::with_runtime(runtime)
.config(config.clone()) .config(config.clone())
.create_unbootstrapped() .create_unbootstrapped()
.unwrap(); .unwrap();
Self { Self {
running_services: Arc::new(RwLock::new(BTreeMap::new())), running_services: Arc::new(RwLock::new(BTreeMap::new())),
starting_services: Arc::new(RwLock::new(BTreeSet::new())), starting_services: Arc::new(RwLock::new(BTreeSet::new())),
@ -97,19 +97,18 @@ impl Tor {
/// Create Tor client configuration. /// Create Tor client configuration.
fn build_config() -> TorClientConfig { fn build_config() -> TorClientConfig {
// Create Tor client config. // Create Tor client config.
let mut builder = let mut builder = TorClientConfigBuilder::from_directories(
TorClientConfigBuilder::from_directories(TorConfig::state_path(), TorConfig::state_path(),
TorConfig::cache_path()); TorConfig::cache_path(),
);
// Setup bridges. // Setup bridges.
let bridge = TorConfig::get_bridge(); let bridge = TorConfig::get_bridge();
if let Some(b) = bridge { if let Some(b) = bridge {
match b { match b {
super::TorBridge::Snowflake(path, conn) => { super::TorBridge::Snowflake(path, conn) => {
Self::build_snowflake(&mut builder, path, conn) Self::build_snowflake(&mut builder, path, conn)
}, }
super::TorBridge::Obfs4(path, conn) => { super::TorBridge::Obfs4(path, conn) => Self::build_obfs4(&mut builder, path, conn),
Self::build_obfs4(&mut builder, path, conn)
},
} }
} }
// Setup address filter. // Setup address filter.
@ -123,7 +122,10 @@ impl Tor {
pub fn rebuild_client() { pub fn rebuild_client() {
let config = Self::build_config(); let config = Self::build_config();
let r_client = TOR_SERVER_STATE.client_config.read(); let r_client = TOR_SERVER_STATE.client_config.read();
r_client.0.reconfigure(&config, tor_config::Reconfigure::AllOrNothing).unwrap(); r_client
.0
.reconfigure(&config, tor_config::Reconfigure::AllOrNothing)
.unwrap();
} }
/// Send post request using Tor. /// Send post request using Tor.
@ -144,15 +146,11 @@ impl Tor {
// Send request. // Send request.
let mut resp = None; let mut resp = None;
match http.request(req).await { match http.request(req).await {
Ok(r) => { Ok(r) => match hyper::body::to_bytes(r).await {
match hyper::body::to_bytes(r).await { Ok(raw) => resp = Some(String::from_utf8_lossy(&raw).to_string()),
Ok(raw) => { Err(_) => {}
resp = Some(String::from_utf8_lossy(&raw).to_string())
},
Err(_) => {},
}
}, },
Err(_) => {}, Err(_) => {}
} }
resp resp
} }
@ -218,111 +216,143 @@ impl Tor {
let service_id = id.clone(); let service_id = id.clone();
thread::spawn(move || { thread::spawn(move || {
let on_error = |service_id: String| {
// Remove service from starting.
let mut w_services = TOR_SERVER_STATE.starting_services.write();
w_services.remove(&service_id);
// Save failed service.
let mut w_services = TOR_SERVER_STATE.failed_services.write();
w_services.insert(service_id);
};
let (client, config) = Self::client_config(); let (client, config) = Self::client_config();
let client_thread = client.clone(); let client_thread = client.clone();
client.runtime().spawn(async move { client
// Add service key to keystore. .runtime()
let hs_nickname = HsNickname::new(service_id.clone()).unwrap(); .spawn(async move {
Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname); // Add service key to keystore.
// Bootstrap client. let hs_nickname = HsNickname::new(service_id.clone()).unwrap();
client_thread.bootstrap().await.unwrap(); if let Err(_) = Self::add_service_key(config.fs_mistrust(),
// Launch Onion service. &key,
let service_config = OnionServiceConfigBuilder::default() &hs_nickname) {
.nickname(hs_nickname.clone()) on_error(service_id);
.build()
.unwrap();
if let Ok((service, request)) = client_thread.launch_onion_service(service_config) {
// Launch service proxy.
let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port);
tokio::spawn(
Self::run_service_proxy(addr,
client_thread.clone(),
service.clone(),
request,
hs_nickname.clone())
).await.unwrap();
// Check service availability if not checking.
if Self::is_service_checking(&service_id) {
return; return;
} }
let client_check = client_thread.clone(); // Bootstrap client.
let url = format!("http://{}/v2/foreign", service.onion_name().unwrap().to_string()); client_thread.bootstrap().await.unwrap();
thread::spawn(move || { // Launch Onion service.
// Wait 1 second to start. let service_config = OnionServiceConfigBuilder::default()
thread::sleep(Duration::from_millis(1000)); .nickname(hs_nickname.clone())
let runtime = client_thread.runtime(); .build()
// Put service to checking. .unwrap();
{ if let Ok((service, request)) =
let mut w_services = TOR_SERVER_STATE.checking_services.write(); client_thread.launch_onion_service(service_config)
w_services.insert(service_id.clone()); {
} // Launch service proxy.
runtime let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port);
.spawn(async move { tokio::spawn(Self::run_service_proxy(
const MAX_ERRORS: i32 = 3; addr,
let mut errors_count = 0; client_thread.clone(),
loop { service.clone(),
if !Self::is_service_running(&service_id) { request,
// Remove service from checking. hs_nickname.clone(),
let mut w_services = TOR_SERVER_STATE.checking_services.write(); ))
w_services.remove(&service_id); .await
break; .unwrap();
}
let data = json!({
"id": 1,
"jsonrpc": "2.0",
"method": "check_version",
"params": []
}).to_string();
// Bootstrap client.
client_check.bootstrap().await.unwrap();
// Create http tor-powered client to post data.
let tls_connector = TlsConnector::builder().unwrap().build().unwrap();
let tor_connector = ArtiHttpConnector::new(client_check.clone(), tls_connector);
let http = hyper::Client::builder().build::<_, Body>(tor_connector);
// Create request.
let req = hyper::Request::builder()
.method(hyper::Method::POST)
.uri(url.clone())
.body(Body::from(data))
.unwrap();
// Send request.
let duration = match http.request(req).await {
Ok(_) => {
// Remove service from starting.
let mut w_services = TOR_SERVER_STATE.starting_services.write();
w_services.remove(&service_id);
// Check again after 15 seconds.
Duration::from_millis(15000)
},
Err(_) => {
// Restart service on 3rd error.
errors_count += 1;
if errors_count == MAX_ERRORS {
errors_count = 0;
let key = key.clone();
let service_id = service_id.clone();
thread::spawn(move || {
Self::restart_service(port, key, &service_id);
});
}
Duration::from_millis(5000)
},
};
sleep(duration).await;
}
}).unwrap();
});
} else {
// Remove service from starting.
let mut w_services = TOR_SERVER_STATE.starting_services.write();
w_services.remove(&service_id);
// Save failed service.
let mut w_services = TOR_SERVER_STATE.failed_services.write();
w_services.insert(service_id);
}
}).unwrap(); // Check service availability if not checking.
if Self::is_service_checking(&service_id) {
return;
}
let client_check = client_thread.clone();
let url = format!(
"http://{}/v2/foreign",
service.onion_name().unwrap().to_string()
);
thread::spawn(move || {
// Wait 1 second to start.
thread::sleep(Duration::from_millis(1000));
let runtime = client_thread.runtime();
// Put service to checking.
{
let mut w_services = TOR_SERVER_STATE.checking_services.write();
w_services.insert(service_id.clone());
}
runtime
.spawn(async move {
const MAX_ERRORS: i32 = 3;
let mut errors_count = 0;
loop {
if !Self::is_service_running(&service_id) {
// Remove service from checking.
let mut w_services =
TOR_SERVER_STATE.checking_services.write();
w_services.remove(&service_id);
break;
}
let data = json!({
"id": 1,
"jsonrpc": "2.0",
"method": "check_version",
"params": []
})
.to_string();
// Bootstrap client.
client_check.bootstrap().await.unwrap();
// Create http tor-powered client to post data.
let tls_connector =
TlsConnector::builder().unwrap().build().unwrap();
let tor_connector = ArtiHttpConnector::new(
client_check.clone(),
tls_connector,
);
let http = hyper::Client::builder()
.build::<_, Body>(tor_connector);
// Create request.
let req = hyper::Request::builder()
.method(hyper::Method::POST)
.uri(url.clone())
.body(Body::from(data))
.unwrap();
// Send request.
let duration = match http.request(req).await {
Ok(_) => {
// Remove service from starting.
let mut w_services =
TOR_SERVER_STATE.starting_services.write();
w_services.remove(&service_id);
// Check again after 15 seconds.
Duration::from_millis(15000)
}
Err(_) => {
// Restart service on 3rd error.
errors_count += 1;
if errors_count == MAX_ERRORS {
errors_count = 0;
let key = key.clone();
let service_id = service_id.clone();
thread::spawn(move || {
Self::restart_service(
port,
key,
&service_id,
);
});
}
Duration::from_millis(5000)
}
};
// Wait to check service again.
sleep(duration).await;
}
})
.unwrap();
});
return;
}
on_error(service_id);
})
.unwrap();
}); });
} }
@ -332,11 +362,10 @@ impl Tor {
client: TorClient<R>, client: TorClient<R>,
service: Arc<RunningOnionService>, service: Arc<RunningOnionService>,
request: S, request: S,
nickname: HsNickname nickname: HsNickname,
) ) where
where R: Runtime,
R: Runtime, S: futures::Stream<Item = tor_hsservice::RendRequest> + Unpin + Send + 'static,
S: futures::Stream<Item = tor_hsservice::RendRequest> + Unpin + Send + 'static,
{ {
let id = nickname.to_string(); let id = nickname.to_string();
let runtime = client.runtime().clone(); let runtime = client.runtime().clone();
@ -360,65 +389,60 @@ impl Tor {
.spawn(async move { .spawn(async move {
match proxy match proxy
.handle_requests(runtime, nickname.clone(), request) .handle_requests(runtime, nickname.clone(), request)
.await { .await
{
Ok(()) => { Ok(()) => {
// Remove service from running. // Remove service from running.
let mut w_services = let mut w_services = TOR_SERVER_STATE.running_services.write();
TOR_SERVER_STATE.running_services.write();
w_services.remove(&id); w_services.remove(&id);
} }
Err(_) => { Err(_) => {
// Remove service from running. // Remove service from running.
let mut w_services = let mut w_services = TOR_SERVER_STATE.running_services.write();
TOR_SERVER_STATE.running_services.write();
w_services.remove(&id); w_services.remove(&id);
// Save failed service. // Save failed service.
let mut w_services = let mut w_services = TOR_SERVER_STATE.failed_services.write();
TOR_SERVER_STATE.failed_services.write();
w_services.insert(id); w_services.insert(id);
} }
} }
}).unwrap(); })
.unwrap();
} }
/// Save Onion service key to keystore. /// Save Onion service key to keystore.
fn add_service_key(mistrust: &Mistrust, key: &SecretKey, hs_nickname: &HsNickname) { fn add_service_key(
mistrust: &Mistrust,
key: &SecretKey,
hs_nickname: &HsNickname,
) -> tor_keymgr::Result<()> {
let arti_store = let arti_store =
ArtiNativeKeystore::from_path_and_mistrust(TorConfig::keystore_path(), &mistrust) ArtiNativeKeystore::from_path_and_mistrust(TorConfig::keystore_path(), &mistrust)?;
.unwrap();
let key_manager = KeyMgrBuilder::default() let key_manager = KeyMgrBuilder::default()
.default_store(Box::new(arti_store)) .default_store(Box::new(arti_store))
.build() .build()
.unwrap(); .unwrap();
let expanded_sk = ExpandedSecretKey::from_bytes( let expanded_sk =
Sha512::default() ExpandedSecretKey::from_bytes(Sha512::default().chain_update(key).finalize().as_ref());
.chain_update(key)
.finalize()
.as_ref(),
);
let mut sk_bytes = [0_u8; 64]; let mut sk_bytes = [0_u8; 64];
sk_bytes[0..32].copy_from_slice(&expanded_sk.scalar.to_bytes()); sk_bytes[0..32].copy_from_slice(&expanded_sk.scalar.to_bytes());
sk_bytes[32..64].copy_from_slice(&expanded_sk.hash_prefix); sk_bytes[32..64].copy_from_slice(&expanded_sk.hash_prefix);
let expanded_kp = ExpandedKeypair::from_secret_key_bytes(sk_bytes).unwrap(); let expanded_kp = ExpandedKeypair::from_secret_key_bytes(sk_bytes).unwrap();
key_manager key_manager.insert(
.insert( HsIdKey::from(expanded_kp.public().clone()),
HsIdKey::from(expanded_kp.public().clone()), &HsIdPublicKeySpecifier::new(hs_nickname.clone()),
&HsIdPublicKeySpecifier::new(hs_nickname.clone()), KeystoreSelector::Default,
KeystoreSelector::Default, )?;
)
.unwrap();
key_manager key_manager.insert(
.insert( HsIdKeypair::from(expanded_kp),
HsIdKeypair::from(expanded_kp), &HsIdKeypairSpecifier::new(hs_nickname.clone()),
&HsIdKeypairSpecifier::new(hs_nickname.clone()), KeystoreSelector::Default,
KeystoreSelector::Default, )?;
) Ok(())
.unwrap();
} }
fn build_snowflake(builder: &mut TorClientConfigBuilder, bin_path: String, conn_line: String) { fn build_snowflake(builder: &mut TorClientConfigBuilder, bin_path: String, conn_line: String) {
@ -454,4 +478,4 @@ impl Tor {
.run_on_startup(true); .run_on_startup(true);
builder.bridges().transports().push(transport); builder.bridges().transports().push(transport);
} }
} }