tor: add service key result check
This commit is contained in:
parent
eec434ecaa
commit
c4fa0f7ec6
1 changed files with 180 additions and 156 deletions
336
src/tor/tor.rs
336
src/tor/tor.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue