2021-05-22 20:06:05 -07:00
|
|
|
use std::str::FromStr;
|
2021-04-22 21:34:23 -07:00
|
|
|
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;
|
2021-03-22 20:19:56 -07:00
|
|
|
use log::{error, info, warn};
|
2021-04-23 21:56:58 -07:00
|
|
|
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-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
|
|
|
}
|
|
|
|
|
2021-04-22 21:34:23 -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,
|
2021-05-19 17:33:39 -07:00
|
|
|
#[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 {
|
2021-05-19 17:33:39 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-04-22 21:34:23 -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
|
|
|
})
|
|
|
|
}
|
2021-05-19 17:33:39 -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,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|