Done implementing everything
This commit is contained in:
parent
a124a26d36
commit
5d32c35ad7
2 changed files with 215 additions and 24 deletions
207
src/jobs.rs
207
src/jobs.rs
|
@ -1,16 +1,13 @@
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rquickjs::{async_with, AsyncContext, AsyncRuntime, Exception, FromJs, IntoJs};
|
use rquickjs::{async_with, AsyncContext, AsyncRuntime};
|
||||||
use std::{num::NonZeroUsize, pin::Pin, sync::Arc, thread::available_parallelism};
|
use std::{num::NonZeroUsize, sync::Arc, thread::available_parallelism};
|
||||||
use tokio::{
|
use tokio::{io::AsyncWriteExt, runtime::Handle, sync::Mutex, task::block_in_place};
|
||||||
io::{AsyncWrite, AsyncWriteExt, BufWriter},
|
|
||||||
net::{unix::WriteHalf, UnixStream},
|
|
||||||
runtime::Handle,
|
|
||||||
sync::Mutex,
|
|
||||||
task::block_in_place,
|
|
||||||
};
|
|
||||||
use tub::Pool;
|
use tub::Pool;
|
||||||
|
|
||||||
use crate::consts::{NSIG_FUNCTION_ARRAY, NSIG_FUNCTION_NAME, REGEX_PLAYER_ID, TEST_YOUTUBE_VIDEO};
|
use crate::consts::{
|
||||||
|
NSIG_FUNCTION_ARRAY, NSIG_FUNCTION_NAME, REGEX_HELPER_OBJ_NAME, REGEX_PLAYER_ID,
|
||||||
|
REGEX_SIGNATURE_FUNCTION, REGEX_SIGNATURE_TIMESTAMP, TEST_YOUTUBE_VIDEO,
|
||||||
|
};
|
||||||
|
|
||||||
pub enum JobOpcode {
|
pub enum JobOpcode {
|
||||||
ForceUpdate,
|
ForceUpdate,
|
||||||
|
@ -45,13 +42,18 @@ impl From<u8> for JobOpcode {
|
||||||
|
|
||||||
pub struct PlayerInfo {
|
pub struct PlayerInfo {
|
||||||
nsig_function_code: String,
|
nsig_function_code: String,
|
||||||
|
sig_function_code: String,
|
||||||
|
sig_function_name: String,
|
||||||
|
signature_timestamp: u64,
|
||||||
player_id: u32,
|
player_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct JavascriptInterpreter {
|
pub struct JavascriptInterpreter {
|
||||||
js_runtime: AsyncRuntime,
|
js_runtime: AsyncRuntime,
|
||||||
|
sig_context: AsyncContext,
|
||||||
nsig_context: AsyncContext,
|
nsig_context: AsyncContext,
|
||||||
player_id: Mutex<u32>,
|
sig_player_id: Mutex<u32>,
|
||||||
|
nsig_player_id: Mutex<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JavascriptInterpreter {
|
impl JavascriptInterpreter {
|
||||||
|
@ -63,10 +65,17 @@ impl JavascriptInterpreter {
|
||||||
.block_on(AsyncContext::full(&js_runtime))
|
.block_on(AsyncContext::full(&js_runtime))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
let sig_context = block_in_place(|| {
|
||||||
|
Handle::current()
|
||||||
|
.block_on(AsyncContext::full(&js_runtime))
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
JavascriptInterpreter {
|
JavascriptInterpreter {
|
||||||
js_runtime: js_runtime,
|
js_runtime,
|
||||||
nsig_context: nsig_context,
|
sig_context,
|
||||||
player_id: Mutex::new(0),
|
nsig_context,
|
||||||
|
sig_player_id: Mutex::new(0),
|
||||||
|
nsig_player_id: Mutex::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +100,10 @@ impl GlobalState {
|
||||||
GlobalState {
|
GlobalState {
|
||||||
player_info: Mutex::new(PlayerInfo {
|
player_info: Mutex::new(PlayerInfo {
|
||||||
nsig_function_code: Default::default(),
|
nsig_function_code: Default::default(),
|
||||||
|
sig_function_code: Default::default(),
|
||||||
|
sig_function_name: Default::default(),
|
||||||
player_id: Default::default(),
|
player_id: Default::default(),
|
||||||
|
signature_timestamp: Default::default(),
|
||||||
}),
|
}),
|
||||||
js_runtime_pool: runtime_pool,
|
js_runtime_pool: runtime_pool,
|
||||||
}
|
}
|
||||||
|
@ -113,13 +125,14 @@ pub async fn process_fetch_update<W>(
|
||||||
W: tokio::io::AsyncWrite + Unpin + Send,
|
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||||
{
|
{
|
||||||
let cloned_writer = stream.clone();
|
let cloned_writer = stream.clone();
|
||||||
let mut writer = cloned_writer.lock().await;
|
let mut writer;
|
||||||
|
|
||||||
let global_state = state.clone();
|
let global_state = state.clone();
|
||||||
let response = match reqwest::get(TEST_YOUTUBE_VIDEO).await {
|
let response = match reqwest::get(TEST_YOUTUBE_VIDEO).await {
|
||||||
Ok(req) => req.text().await.unwrap(),
|
Ok(req) => req.text().await.unwrap(),
|
||||||
Err(x) => {
|
Err(x) => {
|
||||||
println!("Could not fetch the test video: {}", x);
|
println!("Could not fetch the test video: {}", x);
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -128,6 +141,7 @@ pub async fn process_fetch_update<W>(
|
||||||
let player_id_str = match REGEX_PLAYER_ID.captures(&response).unwrap().get(1) {
|
let player_id_str = match REGEX_PLAYER_ID.captures(&response).unwrap().get(1) {
|
||||||
Some(result) => result.as_str(),
|
Some(result) => result.as_str(),
|
||||||
None => {
|
None => {
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +156,7 @@ pub async fn process_fetch_update<W>(
|
||||||
|
|
||||||
if player_id == current_player_id {
|
if player_id == current_player_id {
|
||||||
// Player is already up to date
|
// Player is already up to date
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
writer.write_u32(request_id).await;
|
writer.write_u32(request_id).await;
|
||||||
writer.write_u16(0xFFFF).await;
|
writer.write_u16(0xFFFF).await;
|
||||||
return;
|
return;
|
||||||
|
@ -156,6 +171,7 @@ pub async fn process_fetch_update<W>(
|
||||||
Ok(req) => req.text().await.unwrap(),
|
Ok(req) => req.text().await.unwrap(),
|
||||||
Err(x) => {
|
Err(x) => {
|
||||||
println!("Could not fetch the player JS: {}", x);
|
println!("Could not fetch the player JS: {}", x);
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -179,6 +195,7 @@ pub async fn process_fetch_update<W>(
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(x) => {
|
Err(x) => {
|
||||||
println!("Error: nsig regex compilation failed: {}", x);
|
println!("Error: nsig regex compilation failed: {}", x);
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +207,7 @@ pub async fn process_fetch_update<W>(
|
||||||
.get(1)
|
.get(1)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_str()
|
.as_str()
|
||||||
.split(",");
|
.split(',');
|
||||||
|
|
||||||
let array_values: Vec<&str> = array_content.collect();
|
let array_values: Vec<&str> = array_content.collect();
|
||||||
|
|
||||||
|
@ -215,11 +232,69 @@ pub async fn process_fetch_update<W>(
|
||||||
.as_str();
|
.as_str();
|
||||||
|
|
||||||
// Extract signature function name
|
// Extract signature function name
|
||||||
|
let sig_function_name = REGEX_SIGNATURE_FUNCTION
|
||||||
|
.captures(&player_javascript)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str();
|
||||||
|
|
||||||
|
let mut sig_function_body_regex_str: String = String::new();
|
||||||
|
sig_function_body_regex_str += sig_function_name;
|
||||||
|
sig_function_body_regex_str += "=function\\([a-zA-Z0-9_]+\\)\\{.+?\\}";
|
||||||
|
|
||||||
|
let sig_function_body_regex = Regex::new(&sig_function_body_regex_str).unwrap();
|
||||||
|
|
||||||
|
let sig_function_body = sig_function_body_regex
|
||||||
|
.captures(&player_javascript)
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.as_str();
|
||||||
|
|
||||||
|
// Get the helper object
|
||||||
|
let helper_object_name = REGEX_HELPER_OBJ_NAME
|
||||||
|
.captures(sig_function_body)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str();
|
||||||
|
|
||||||
|
let mut helper_object_body_regex_str = String::new();
|
||||||
|
helper_object_body_regex_str += "var ";
|
||||||
|
helper_object_body_regex_str += helper_object_name;
|
||||||
|
helper_object_body_regex_str += "=\\{(?>.|\\n)+?\\}\\};";
|
||||||
|
|
||||||
|
let helper_object_body_regex = Regex::new(&helper_object_body_regex_str).unwrap();
|
||||||
|
let helper_object_body = helper_object_body_regex
|
||||||
|
.captures(&player_javascript)
|
||||||
|
.unwrap()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.as_str();
|
||||||
|
|
||||||
|
let mut sig_code = String::new();
|
||||||
|
sig_code += helper_object_body;
|
||||||
|
sig_code += sig_function_body;
|
||||||
|
|
||||||
|
// Get signature timestamp
|
||||||
|
let signature_timestamp: u64 = REGEX_SIGNATURE_TIMESTAMP
|
||||||
|
.captures(&player_javascript)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
current_player_info = global_state.player_info.lock().await;
|
current_player_info = global_state.player_info.lock().await;
|
||||||
current_player_info.player_id = player_id;
|
current_player_info.player_id = player_id;
|
||||||
current_player_info.nsig_function_code = nsig_function_code;
|
current_player_info.nsig_function_code = nsig_function_code;
|
||||||
|
current_player_info.sig_function_code = sig_code;
|
||||||
|
current_player_info.sig_function_name = sig_function_name.to_string();
|
||||||
|
current_player_info.signature_timestamp = signature_timestamp;
|
||||||
|
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
writer.write_u32(request_id).await;
|
writer.write_u32(request_id).await;
|
||||||
// sync code to tell the client the player had updated
|
// sync code to tell the client the player had updated
|
||||||
writer.write_u16(0xF44F).await;
|
writer.write_u16(0xF44F).await;
|
||||||
|
@ -238,7 +313,6 @@ pub async fn process_decrypt_n_signature<W>(
|
||||||
W: tokio::io::AsyncWrite + Unpin + Send,
|
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||||
{
|
{
|
||||||
let cloned_writer = stream.clone();
|
let cloned_writer = stream.clone();
|
||||||
let mut writer = cloned_writer.lock().await;
|
|
||||||
let global_state = state.clone();
|
let global_state = state.clone();
|
||||||
|
|
||||||
println!("Signature to be decrypted: {}", sig);
|
println!("Signature to be decrypted: {}", sig);
|
||||||
|
@ -246,7 +320,8 @@ pub async fn process_decrypt_n_signature<W>(
|
||||||
|
|
||||||
let cloned_interp = interp.clone();
|
let cloned_interp = interp.clone();
|
||||||
async_with!(cloned_interp.nsig_context => |ctx|{
|
async_with!(cloned_interp.nsig_context => |ctx|{
|
||||||
let mut current_player_id = interp.player_id.lock().await;
|
let mut writer;
|
||||||
|
let mut current_player_id = interp.nsig_player_id.lock().await;
|
||||||
let player_info = global_state.player_info.lock().await;
|
let player_info = global_state.player_info.lock().await;
|
||||||
|
|
||||||
if player_info.player_id != *current_player_id {
|
if player_info.player_id != *current_player_id {
|
||||||
|
@ -258,6 +333,7 @@ pub async fn process_decrypt_n_signature<W>(
|
||||||
} else {
|
} else {
|
||||||
println!("JavaScript interpreter error (nsig code): {}", n);
|
println!("JavaScript interpreter error (nsig code): {}", n);
|
||||||
}
|
}
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -280,18 +356,111 @@ pub async fn process_decrypt_n_signature<W>(
|
||||||
} else {
|
} else {
|
||||||
println!("JavaScript interpreter error (nsig code): {}", n);
|
println!("JavaScript interpreter error (nsig code): {}", n);
|
||||||
}
|
}
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
write_failure!(writer, request_id);
|
write_failure!(writer, request_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
|
|
||||||
writer.write_u32(request_id).await;
|
writer.write_u32(request_id).await;
|
||||||
writer.write_u16(u16::try_from(decrypted_string.len()).unwrap()).await;
|
writer.write_u16(u16::try_from(decrypted_string.len()).unwrap()).await;
|
||||||
writer.write_all(decrypted_string.as_bytes()).await;
|
writer.write_all(decrypted_string.as_bytes()).await;
|
||||||
|
|
||||||
writer.flush().await;
|
|
||||||
println!("Decrypted signature: {}", decrypted_string);
|
println!("Decrypted signature: {}", decrypted_string);
|
||||||
|
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn process_decrypt_signature<W>(
|
||||||
|
state: Arc<GlobalState>,
|
||||||
|
sig: String,
|
||||||
|
stream: Arc<Mutex<W>>,
|
||||||
|
request_id: u32,
|
||||||
|
) where
|
||||||
|
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||||
|
{
|
||||||
|
let cloned_writer = stream.clone();
|
||||||
|
let global_state = state.clone();
|
||||||
|
|
||||||
|
let interp = global_state.js_runtime_pool.acquire().await;
|
||||||
|
let cloned_interp = interp.clone();
|
||||||
|
|
||||||
|
async_with!(cloned_interp.sig_context => |ctx|{
|
||||||
|
let mut writer;
|
||||||
|
let mut current_player_id = interp.sig_player_id.lock().await;
|
||||||
|
let player_info = global_state.player_info.lock().await;
|
||||||
|
|
||||||
|
if player_info.player_id != *current_player_id {
|
||||||
|
match ctx.eval::<(),String>(player_info.sig_function_code.clone()) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(n) => {
|
||||||
|
if n.is_exception() {
|
||||||
|
println!("JavaScript interpreter error (sig code): {:?}", ctx.catch().as_exception());
|
||||||
|
} else {
|
||||||
|
println!("JavaScript interpreter error (sig code): {}", n);
|
||||||
|
}
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
|
write_failure!(writer, request_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*current_player_id = player_info.player_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sig_function_name = &player_info.sig_function_name;
|
||||||
|
|
||||||
|
let mut call_string: String = String::new();
|
||||||
|
call_string += sig_function_name;
|
||||||
|
call_string += "(\"";
|
||||||
|
call_string += &sig;
|
||||||
|
call_string += "\")";
|
||||||
|
|
||||||
|
drop(player_info);
|
||||||
|
|
||||||
|
let decrypted_string = match ctx.eval::<String,String>(call_string) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(n) => {
|
||||||
|
if n.is_exception() {
|
||||||
|
println!("JavaScript interpreter error (sig code): {:?}", ctx.catch().as_exception());
|
||||||
|
} else {
|
||||||
|
println!("JavaScript interpreter error (sig code): {}", n);
|
||||||
|
}
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
|
write_failure!(writer, request_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
writer = cloned_writer.lock().await;
|
||||||
|
|
||||||
|
writer.write_u32(request_id).await;
|
||||||
|
writer.write_u16(u16::try_from(decrypted_string.len()).unwrap()).await;
|
||||||
|
writer.write_all(decrypted_string.as_bytes()).await;
|
||||||
|
|
||||||
|
println!("Decrypted signature: {}", decrypted_string);
|
||||||
|
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn process_get_signature_timestamp<W>(
|
||||||
|
state: Arc<GlobalState>,
|
||||||
|
stream: Arc<Mutex<W>>,
|
||||||
|
request_id: u32,
|
||||||
|
) where
|
||||||
|
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||||
|
{
|
||||||
|
let cloned_writer = stream.clone();
|
||||||
|
let global_state = state.clone();
|
||||||
|
|
||||||
|
let player_info = global_state.player_info.lock().await;
|
||||||
|
let timestamp = player_info.signature_timestamp;
|
||||||
|
|
||||||
|
let mut writer = cloned_writer.lock().await;
|
||||||
|
|
||||||
|
writer.write_u32(request_id).await;
|
||||||
|
writer.write_u64(timestamp).await;
|
||||||
|
}
|
||||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -5,14 +5,13 @@ use consts::DEFAULT_SOCK_PATH;
|
||||||
use jobs::{process_decrypt_n_signature, process_fetch_update, GlobalState, JobOpcode};
|
use jobs::{process_decrypt_n_signature, process_fetch_update, GlobalState, JobOpcode};
|
||||||
use std::{env::args, io::Error, sync::Arc};
|
use std::{env::args, io::Error, sync::Arc};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{self, AsyncReadExt, BufReader, BufStream, BufWriter},
|
io::{self, AsyncReadExt, BufReader, BufWriter},
|
||||||
net::{
|
net::{UnixListener, UnixStream},
|
||||||
unix::{ReadHalf, WriteHalf},
|
|
||||||
UnixListener, UnixStream,
|
|
||||||
},
|
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::jobs::{process_decrypt_signature, process_get_signature_timestamp};
|
||||||
|
|
||||||
macro_rules! break_fail {
|
macro_rules! break_fail {
|
||||||
($res:expr) => {
|
($res:expr) => {
|
||||||
match $res {
|
match $res {
|
||||||
|
@ -107,6 +106,29 @@ async fn process_socket(state: Arc<GlobalState>, socket: UnixStream) -> Result<(
|
||||||
process_decrypt_n_signature(cloned_state, str, cloned_stream, request_id).await;
|
process_decrypt_n_signature(cloned_state, str, cloned_stream, request_id).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
JobOpcode::DecryptSignature => {
|
||||||
|
let sig_size: usize = usize::from(eof_fail!(
|
||||||
|
inside_readstream.read_u16().await,
|
||||||
|
inside_readstream
|
||||||
|
));
|
||||||
|
let mut buf = vec![0u8; sig_size];
|
||||||
|
|
||||||
|
break_fail!(inside_readstream.read_exact(&mut buf).await);
|
||||||
|
|
||||||
|
let str = break_fail!(String::from_utf8(buf));
|
||||||
|
let cloned_state = state.clone();
|
||||||
|
let cloned_stream = cloned_writestream.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
process_decrypt_signature(cloned_state, str, cloned_stream, request_id).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
JobOpcode::GetSignatureTimestamp => {
|
||||||
|
let cloned_state = state.clone();
|
||||||
|
let cloned_stream = cloned_writestream.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
process_get_signature_timestamp(cloned_state, cloned_stream, request_id).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue