2021-04-22 21:34:23 -07:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::Arc;
|
2021-03-22 14:47:56 -07:00
|
|
|
|
2021-03-25 21:07:32 -07:00
|
|
|
use crate::config::{CliArgs, SEND_SERVER_VERSION, VALIDATE_TOKENS};
|
2021-03-22 14:47:56 -07:00
|
|
|
use crate::ping::{Request, Response, Tls, CONTROL_CENTER_PING_URL};
|
2021-03-22 20:19:56 -07:00
|
|
|
use log::{error, info, warn};
|
2021-03-22 14:47:56 -07:00
|
|
|
use parking_lot::RwLock;
|
2021-03-25 21:07:32 -07:00
|
|
|
use rustls::sign::CertifiedKey;
|
2021-03-22 14:47:56 -07:00
|
|
|
use rustls::ResolvesServerCert;
|
|
|
|
use sodiumoxide::crypto::box_::PrecomputedKey;
|
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,
|
|
|
|
pub tls_config: Tls,
|
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 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-03-22 14:47:56 -07:00
|
|
|
impl ServerState {
|
2021-04-23 15:03:53 -07:00
|
|
|
pub async fn init(secret: &str, config: &CliArgs) -> 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;
|
|
|
|
|
2021-03-25 19:58:07 -07:00
|
|
|
if config.enable_server_string {
|
|
|
|
warn!("Client will send Server header in responses. This is not recommended!");
|
|
|
|
SEND_SERVER_VERSION.store(true, Ordering::Release);
|
|
|
|
}
|
2021-03-25 18:06:54 -07:00
|
|
|
|
2021-03-22 14:47:56 -07:00
|
|
|
match resp {
|
|
|
|
Ok(resp) => match resp.json::<Response>().await {
|
2021-04-18 20:06:18 -07:00
|
|
|
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-04-22 21:34:23 -07:00
|
|
|
PREVIOUSLY_COMPROMISED.store(resp.paused, 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-18 20:06:18 -07:00
|
|
|
if config.disable_token_validation {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
precomputed_key: key,
|
|
|
|
image_server: resp.image_server,
|
|
|
|
tls_config: resp.tls.unwrap(),
|
|
|
|
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
|
|
|
})
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Got malformed response: {}", 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() => {
|
|
|
|
error!("Response timed out to control server. Is MangaDex down?");
|
2021-04-23 15:03:53 -07:00
|
|
|
Err(ServerInitError::Timeout(e))
|
2021-03-22 14:47:56 -07:00
|
|
|
}
|
|
|
|
e => {
|
|
|
|
warn!("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
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RwLockServerState(pub RwLock<ServerState>);
|
|
|
|
|
|
|
|
impl ResolvesServerCert for RwLockServerState {
|
|
|
|
fn resolve(&self, _: rustls::ClientHello) -> Option<CertifiedKey> {
|
|
|
|
let read_guard = self.0.read();
|
|
|
|
Some(CertifiedKey {
|
2021-03-25 21:07:32 -07:00
|
|
|
cert: read_guard.tls_config.certs.clone(),
|
|
|
|
key: Arc::clone(&read_guard.tls_config.priv_key),
|
2021-03-22 14:47:56 -07:00
|
|
|
ocsp: None,
|
|
|
|
sct_list: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|