2021-03-26 04:07:32 +00:00
|
|
|
use std::{io::BufReader, sync::Arc};
|
2021-03-26 01:06:54 +00:00
|
|
|
use std::{
|
|
|
|
num::{NonZeroU16, NonZeroUsize},
|
2021-03-26 02:58:07 +00:00
|
|
|
sync::atomic::Ordering,
|
2021-03-26 01:06:54 +00:00
|
|
|
};
|
2021-03-18 01:45:16 +00:00
|
|
|
|
2021-03-23 03:19:56 +00:00
|
|
|
use log::{error, info, warn};
|
2021-03-26 04:07:32 +00:00
|
|
|
use rustls::{
|
|
|
|
internal::pemfile::{certs, rsa_private_keys},
|
|
|
|
sign::RSASigningKey,
|
|
|
|
};
|
|
|
|
use rustls::{sign::SigningKey, Certificate};
|
|
|
|
use serde::{
|
|
|
|
de::{MapAccess, Visitor},
|
|
|
|
Deserialize, Serialize,
|
|
|
|
};
|
2021-03-18 01:45:16 +00:00
|
|
|
use sodiumoxide::crypto::box_::PrecomputedKey;
|
|
|
|
use url::Url;
|
|
|
|
|
2021-03-26 02:58:07 +00:00
|
|
|
use crate::config::VALIDATE_TOKENS;
|
2021-03-22 21:47:56 +00:00
|
|
|
use crate::state::RwLockServerState;
|
2021-03-26 01:06:54 +00:00
|
|
|
use crate::{client_api_version, config::CliArgs};
|
2021-03-18 01:45:16 +00:00
|
|
|
|
2021-03-22 21:47:56 +00:00
|
|
|
pub const CONTROL_CENTER_PING_URL: &str = "https://api.mangadex.network/ping";
|
|
|
|
|
|
|
|
#[derive(Serialize, Debug)]
|
2021-03-18 02:41:48 +00:00
|
|
|
pub struct Request<'a> {
|
2021-03-18 01:45:16 +00:00
|
|
|
secret: &'a str,
|
2021-03-26 01:06:54 +00:00
|
|
|
port: NonZeroU16,
|
2021-03-18 01:45:16 +00:00
|
|
|
disk_space: usize,
|
2021-03-26 01:06:54 +00:00
|
|
|
network_speed: NonZeroUsize,
|
2021-03-18 01:45:16 +00:00
|
|
|
build_version: usize,
|
|
|
|
tls_created_at: Option<String>,
|
|
|
|
}
|
|
|
|
|
2021-03-18 02:41:48 +00:00
|
|
|
impl<'a> Request<'a> {
|
2021-03-26 01:06:54 +00:00
|
|
|
fn from_config_and_state(
|
|
|
|
secret: &'a str,
|
|
|
|
config: &CliArgs,
|
|
|
|
state: &Arc<RwLockServerState>,
|
|
|
|
) -> Self {
|
2021-03-18 01:45:16 +00:00
|
|
|
Self {
|
2021-03-26 01:06:54 +00:00
|
|
|
secret,
|
2021-03-18 01:45:16 +00:00
|
|
|
port: config.port,
|
|
|
|
disk_space: config.disk_quota,
|
|
|
|
network_speed: config.network_speed,
|
|
|
|
build_version: client_api_version!().parse().unwrap(),
|
|
|
|
tls_created_at: Some(state.0.read().tls_config.created_at.clone()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-23 00:00:21 +00:00
|
|
|
#[allow(clippy::fallible_impl_from)]
|
2021-03-26 01:06:54 +00:00
|
|
|
impl<'a> From<(&'a str, &CliArgs)> for Request<'a> {
|
|
|
|
fn from((secret, config): (&'a str, &CliArgs)) -> Self {
|
2021-03-18 01:45:16 +00:00
|
|
|
Self {
|
2021-03-26 01:06:54 +00:00
|
|
|
secret,
|
2021-03-18 01:45:16 +00:00
|
|
|
port: config.port,
|
|
|
|
disk_space: config.disk_quota,
|
|
|
|
network_speed: config.network_speed,
|
|
|
|
build_version: client_api_version!().parse().unwrap(),
|
|
|
|
tls_created_at: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-22 21:47:56 +00:00
|
|
|
#[derive(Deserialize, Debug)]
|
2021-03-18 02:41:48 +00:00
|
|
|
pub struct Response {
|
|
|
|
pub image_server: Url,
|
|
|
|
pub latest_build: usize,
|
|
|
|
pub url: String,
|
|
|
|
pub token_key: Option<String>,
|
|
|
|
pub compromised: bool,
|
|
|
|
pub paused: bool,
|
2021-03-23 03:04:54 +00:00
|
|
|
#[serde(default)]
|
2021-03-22 21:47:56 +00:00
|
|
|
pub force_tokens: bool,
|
2021-03-18 02:41:48 +00:00
|
|
|
pub tls: Option<Tls>,
|
2021-03-18 01:45:16 +00:00
|
|
|
}
|
|
|
|
|
2021-03-18 02:41:48 +00:00
|
|
|
pub struct Tls {
|
2021-03-18 01:45:16 +00:00
|
|
|
pub created_at: String,
|
2021-03-26 04:07:32 +00:00
|
|
|
pub priv_key: Arc<Box<dyn SigningKey>>,
|
|
|
|
pub certs: Vec<Certificate>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Tls {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: serde::Deserializer<'de>,
|
|
|
|
{
|
|
|
|
struct TlsVisitor;
|
|
|
|
|
|
|
|
impl<'de> Visitor<'de> for TlsVisitor {
|
|
|
|
type Value = Tls;
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
formatter.write_str("a tls struct")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
|
|
|
where
|
|
|
|
A: MapAccess<'de>,
|
|
|
|
{
|
|
|
|
let mut created_at = None;
|
|
|
|
let mut priv_key = None;
|
|
|
|
let mut certificates = None;
|
|
|
|
|
|
|
|
while let Some((key, value)) = map.next_entry::<&str, String>()? {
|
|
|
|
match key {
|
|
|
|
"created_at" => created_at = Some(value.to_string()),
|
|
|
|
"private_key" => {
|
|
|
|
priv_key = rsa_private_keys(&mut BufReader::new(value.as_bytes()))
|
|
|
|
.ok()
|
|
|
|
.and_then(|mut v| {
|
|
|
|
v.pop().and_then(|key| RSASigningKey::new(&key).ok())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"certificate" => {
|
|
|
|
certificates = certs(&mut BufReader::new(value.as_bytes())).ok()
|
|
|
|
}
|
|
|
|
_ => (), // Ignore extra fields
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
match (created_at, priv_key, certificates) {
|
|
|
|
(Some(created_at), Some(priv_key), Some(certificates)) => Ok(Tls {
|
|
|
|
created_at,
|
|
|
|
priv_key: Arc::new(Box::new(priv_key)),
|
|
|
|
certs: certificates,
|
|
|
|
}),
|
|
|
|
_ => Err(serde::de::Error::custom("Could not deserialize tls info")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deserializer.deserialize_map(TlsVisitor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for Tls {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("Tls")
|
|
|
|
.field("created_at", &self.created_at)
|
|
|
|
.finish()
|
|
|
|
}
|
2021-03-18 01:45:16 +00:00
|
|
|
}
|
|
|
|
|
2021-03-26 01:06:54 +00:00
|
|
|
pub async fn update_server_state(secret: &str, req: &CliArgs, data: &mut Arc<RwLockServerState>) {
|
|
|
|
let req = Request::from_config_and_state(secret, req, data);
|
2021-03-22 21:47:56 +00:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let resp = client.post(CONTROL_CENTER_PING_URL).json(&req).send().await;
|
2021-03-18 01:45:16 +00:00
|
|
|
match resp {
|
2021-03-22 21:47:56 +00:00
|
|
|
Ok(resp) => match resp.json::<Response>().await {
|
2021-03-18 01:45:16 +00:00
|
|
|
Ok(resp) => {
|
|
|
|
let mut write_guard = data.0.write();
|
|
|
|
|
|
|
|
write_guard.image_server = resp.image_server;
|
|
|
|
|
|
|
|
if let Some(key) = resp.token_key {
|
2021-03-22 21:47:56 +00:00
|
|
|
if let Some(key) = base64::decode(&key)
|
|
|
|
.ok()
|
|
|
|
.and_then(|k| PrecomputedKey::from_slice(&k))
|
|
|
|
{
|
|
|
|
write_guard.precomputed_key = key;
|
|
|
|
} else {
|
|
|
|
error!("Failed to parse token key: got {}", key);
|
2021-03-18 01:45:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-26 02:58:07 +00:00
|
|
|
if VALIDATE_TOKENS.load(Ordering::Acquire) != resp.force_tokens {
|
2021-03-23 03:19:56 +00:00
|
|
|
if resp.force_tokens {
|
|
|
|
info!("Client received command to enforce token validity.");
|
|
|
|
} else {
|
|
|
|
info!("Client received command to no longer enforce token validity");
|
|
|
|
}
|
2021-03-26 02:58:07 +00:00
|
|
|
VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);
|
2021-03-23 03:19:56 +00:00
|
|
|
}
|
2021-03-18 01:45:16 +00:00
|
|
|
|
|
|
|
if let Some(tls) = resp.tls {
|
|
|
|
write_guard.tls_config = tls;
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.compromised {
|
2021-03-23 03:19:56 +00:00
|
|
|
error!("Got compromised response from control center!");
|
2021-03-18 01:45:16 +00:00
|
|
|
}
|
|
|
|
|
2021-03-23 03:19:56 +00:00
|
|
|
if resp.paused != write_guard.log_state.was_paused_before {
|
|
|
|
write_guard.log_state.was_paused_before = resp.paused;
|
|
|
|
if resp.paused {
|
|
|
|
warn!("Control center has paused this node.");
|
|
|
|
} else {
|
|
|
|
info!("Control center is no longer pausing this node.");
|
|
|
|
}
|
2021-03-18 01:45:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if resp.url != write_guard.url {
|
|
|
|
info!("This client's URL has been updated to {}", resp.url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => warn!("Got malformed response: {}", e),
|
|
|
|
},
|
|
|
|
Err(e) => match e {
|
2021-03-22 21:47:56 +00:00
|
|
|
e if e.is_timeout() => {
|
2021-03-18 01:45:16 +00:00
|
|
|
error!("Response timed out to control server. Is MangaDex down?")
|
|
|
|
}
|
|
|
|
e => warn!("Failed to send request: {}", e),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|