mangadex-home-rs/src/state.rs

171 lines
6.5 KiB
Rust
Raw Normal View History

2021-05-22 20:06:05 -07:00
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
2021-03-22 14:47:56 -07:00
2021-07-09 16:48:25 -07:00
use crate::config::{ClientSecret, Config, UnstableOptions, OFFLINE_MODE, VALIDATE_TOKENS};
2021-04-23 21:56:58 -07:00
use crate::ping::{Request, Response, CONTROL_CENTER_PING_URL};
use arc_swap::ArcSwap;
use once_cell::sync::OnceCell;
2021-03-22 14:47:56 -07:00
use parking_lot::RwLock;
2021-04-23 21:56:58 -07:00
use rustls::sign::{CertifiedKey, SigningKey};
use rustls::Certificate;
2021-04-23 20:28:47 -07:00
use rustls::{ClientHello, ResolvesServerCert};
2021-05-22 20:06:05 -07:00
use sodiumoxide::crypto::box_::{PrecomputedKey, PRECOMPUTEDKEYBYTES};
2021-04-23 15:03:53 -07:00
use thiserror::Error;
2021-07-12 20:23:51 -07:00
use tracing::{error, info, warn};
2021-03-22 14:47:56 -07:00
use url::Url;
pub struct ServerState {
pub precomputed_key: PrecomputedKey,
pub image_server: Url,
2021-04-18 20:06:18 -07:00
pub url: Url,
pub url_overridden: bool,
2021-03-22 20:19:56 -07:00
}
pub static PREVIOUSLY_PAUSED: AtomicBool = AtomicBool::new(false);
pub static PREVIOUSLY_COMPROMISED: AtomicBool = AtomicBool::new(false);
2021-03-22 14:47:56 -07:00
2021-04-23 21:56:58 -07:00
pub static TLS_PREVIOUSLY_CREATED: OnceCell<ArcSwap<String>> = OnceCell::new();
pub static TLS_SIGNING_KEY: OnceCell<ArcSwap<Box<dyn SigningKey>>> = OnceCell::new();
pub static TLS_CERTS: OnceCell<ArcSwap<Vec<Certificate>>> = OnceCell::new();
2021-04-23 15:03:53 -07:00
#[derive(Error, Debug)]
pub enum ServerInitError {
#[error(transparent)]
MalformedResponse(reqwest::Error),
#[error(transparent)]
Timeout(reqwest::Error),
#[error(transparent)]
SendFailure(reqwest::Error),
#[error("Failed to parse token key")]
KeyParseError(String),
#[error("Token key was not provided in initial request")]
MissingTokenKey,
#[error("Got error response from control center")]
ErrorResponse,
2021-04-23 15:03:53 -07:00
}
2021-03-22 14:47:56 -07:00
impl ServerState {
2021-07-09 16:48:25 -07:00
pub async fn init(secret: &ClientSecret, config: &Config) -> Result<Self, ServerInitError> {
2021-03-22 14:47:56 -07:00
let resp = reqwest::Client::new()
.post(CONTROL_CENTER_PING_URL)
2021-03-25 18:06:54 -07:00
.json(&Request::from((secret, config)))
2021-03-22 14:47:56 -07:00
.send()
.await;
match resp {
Ok(resp) => match resp.json::<Response>().await {
Ok(Response::Ok(mut resp)) => {
2021-03-22 14:47:56 -07:00
let key = resp
.token_key
2021-04-23 15:03:53 -07:00
.ok_or(ServerInitError::MissingTokenKey)
2021-03-22 14:47:56 -07:00
.and_then(|key| {
if let Some(key) = base64::decode(&key)
.ok()
.and_then(|k| PrecomputedKey::from_slice(&k))
{
2021-04-23 15:03:53 -07:00
Ok(key)
2021-03-22 14:47:56 -07:00
} else {
error!("Failed to parse token key: got {}", key);
2021-04-23 15:03:53 -07:00
Err(ServerInitError::KeyParseError(key))
2021-03-22 14:47:56 -07:00
}
2021-04-23 15:03:53 -07:00
})?;
2021-03-22 14:47:56 -07:00
2021-05-27 13:22:15 -07:00
PREVIOUSLY_COMPROMISED.store(resp.compromised, Ordering::Release);
2021-03-22 14:47:56 -07:00
if resp.compromised {
2021-03-22 20:19:56 -07:00
error!("Got compromised response from control center!");
2021-03-22 14:47:56 -07:00
}
PREVIOUSLY_PAUSED.store(resp.paused, Ordering::Release);
2021-03-22 14:47:56 -07:00
if resp.paused {
2021-03-22 20:19:56 -07:00
warn!("Control center has paused this node!");
2021-03-22 14:47:56 -07:00
}
2021-04-18 20:06:18 -07:00
if let Some(ref override_url) = config.override_upstream {
resp.image_server = override_url.clone();
warn!("Upstream URL overridden to: {}", resp.image_server);
} else {
}
2021-03-22 14:47:56 -07:00
info!("This client's URL has been set to {}", resp.url);
2021-04-25 09:55:31 -07:00
if config
.unstable_options
.contains(&UnstableOptions::DisableTokenValidation)
{
2021-04-18 20:06:18 -07:00
warn!("Token validation is explicitly disabled!");
2021-03-22 20:19:56 -07:00
} else {
2021-04-18 20:06:18 -07:00
if resp.force_tokens {
info!("This client will validate tokens.");
} else {
info!("This client will not validate tokens.");
}
VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);
2021-03-22 14:47:56 -07:00
}
2021-04-23 21:56:58 -07:00
let tls = resp.tls.unwrap();
2021-04-24 09:57:32 -07:00
std::mem::drop(
TLS_PREVIOUSLY_CREATED.set(ArcSwap::from_pointee(tls.created_at)),
);
std::mem::drop(TLS_SIGNING_KEY.set(ArcSwap::new(tls.priv_key)));
std::mem::drop(TLS_CERTS.set(ArcSwap::from_pointee(tls.certs)));
2021-04-23 21:56:58 -07:00
2021-03-22 14:47:56 -07:00
Ok(Self {
precomputed_key: key,
image_server: resp.image_server,
url: resp.url,
2021-04-18 20:06:18 -07:00
url_overridden: config.override_upstream.is_some(),
2021-03-22 14:47:56 -07:00
})
}
Ok(Response::Error(resp)) => {
error!(
"Got an {} error from upstream: {}",
resp.status as u16, resp.error
);
Err(ServerInitError::ErrorResponse)
}
2021-03-22 14:47:56 -07:00
Err(e) => {
2021-04-25 09:55:31 -07:00
error!("Got malformed response: {}. Is MangaDex@Home down?", e);
2021-04-23 15:03:53 -07:00
Err(ServerInitError::MalformedResponse(e))
2021-03-22 14:47:56 -07:00
}
},
Err(e) => match e {
e if e.is_timeout() => {
2021-04-25 09:55:31 -07:00
error!("Response timed out to control server. Is MangaDex@Home down?");
2021-04-23 15:03:53 -07:00
Err(ServerInitError::Timeout(e))
2021-03-22 14:47:56 -07:00
}
e => {
2021-04-25 09:55:31 -07:00
error!("Failed to send request: {}", e);
2021-04-23 15:03:53 -07:00
Err(ServerInitError::SendFailure(e))
2021-03-22 14:47:56 -07:00
}
},
}
}
2021-05-22 20:06:05 -07:00
pub fn init_offline() -> Self {
assert!(OFFLINE_MODE.load(Ordering::Acquire));
Self {
precomputed_key: PrecomputedKey::from_slice(&[41; PRECOMPUTEDKEYBYTES]).unwrap(),
image_server: Url::from_file_path("/dev/null").unwrap(),
url: Url::from_str("http://localhost").unwrap(),
url_overridden: false,
}
}
2021-03-22 14:47:56 -07:00
}
pub struct RwLockServerState(pub RwLock<ServerState>);
2021-04-23 21:56:58 -07:00
pub struct DynamicServerCert;
impl ResolvesServerCert for DynamicServerCert {
2021-04-23 20:28:47 -07:00
fn resolve(&self, _: ClientHello) -> Option<CertifiedKey> {
// TODO: wait for actix-web to use a new version of rustls so we can
// remove cloning the certs all the time
2021-03-22 14:47:56 -07:00
Some(CertifiedKey {
2021-04-23 21:56:58 -07:00
cert: TLS_CERTS.get().unwrap().load().as_ref().clone(),
key: TLS_SIGNING_KEY.get().unwrap().load_full(),
2021-03-22 14:47:56 -07:00
ocsp: None,
sct_list: None,
})
}
}