mirror of
https://github.com/iv-org/inv_sig_helper.git
synced 2024-11-23 05:59:44 +00:00
Improve the program a bit, and include a protocol documentation
This commit is contained in:
parent
48be34ff5c
commit
a124a26d36
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Protocol Format
|
||||
|
||||
All data-types bigger than 1 byte are stored in network endian (big-endian) unless stated otherwise.
|
||||
|
||||
## Request Base
|
||||
| Name | Size (bytes) | Description |
|
||||
|-----------|--------------|--------------------------------------|
|
||||
|opcode | 1 | The operation code to perform, A list of operations currently supported (and their data) can be found in the **Operations** chapter |
|
||||
|request_id | 4 | The ID for the current request, Used to distinguish responses in the current connection |
|
||||
|
||||
The data afterwards depends on the supplied opcode, Please consult the **Operations** chapter for more information.
|
||||
|
||||
## Response Base
|
||||
| Name | Size (bytes) | Description |
|
||||
|------------|--------------|---------------------------------------|
|
||||
|request_id | 4 | The ID for the request that this response is meant for |
|
||||
|
||||
The data afterwards depends on the supplied opcode, Please consult the **Operations** chapter for more information.
|
||||
|
||||
## Operations
|
||||
### `FORCE_UPDATE` (0x00)
|
||||
Forces the server to re-fetch the YouTube player, and extract the necessary components from it (`nsig` function code, `sig` function code, signature timestamp).
|
||||
|
||||
#### Request
|
||||
*No additional data required*
|
||||
|
||||
#### Response
|
||||
| Name | Size (bytes) | Description |
|
||||
|------|--------------|-------------|
|
||||
|status| 4 | The status code of the request: `0xF44F` if successful, `0xFFFF` if no updating is required (YouTube's player ID is equal to the server's current player ID), `0x0000` if an error occurred |
|
||||
|
||||
### `DECRYPT_N_SIGNATURE` (0x01)
|
||||
Decrypt a provided `n` signature using the server's current `nsig` function code, and return the result (or an error).
|
||||
|
||||
#### Request
|
||||
| Name | Size (bytes) | Description |
|
||||
|------|--------------|-------------------------------------|
|
||||
|size | 2 | The size of the encrypted signature |
|
||||
|string| *`size`* | The encrypted signature |
|
||||
|
||||
#### Response
|
||||
| Name | Size (bytes) | Description |
|
||||
|------|--------------|------------------------------------------------------------------|
|
||||
|size | 2 | The size of the decrypted signature, `0x0000` if an error occurred |
|
||||
|string| *`size`* | The decrypted signature |
|
||||
|
@ -8,5 +8,11 @@ pub static REGEX_PLAYER_ID: &Lazy<Regex> = regex!("\\/s\\/player\\/([0-9a-f]{8})
|
||||
pub static NSIG_FUNCTION_ARRAY: &Lazy<Regex> = regex!(
|
||||
"\\.get\\(\"n\"\\)\\)&&\\([a-zA-Z0-9$_]=([a-zA-Z0-9$_]+)(?:\\[(\\d+)])?\\([a-zA-Z0-9$_]\\)"
|
||||
);
|
||||
pub static REGEX_SIGNATURE_TIMESTAMP: &Lazy<Regex> = regex!("signatureTimestamp[=:](\\d+)");
|
||||
|
||||
pub static REGEX_SIGNATURE_FUNCTION: &Lazy<Regex> =
|
||||
regex!("\\bc&&\\(c=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(c\\)\\)");
|
||||
pub static REGEX_HELPER_OBJ_NAME: &Lazy<Regex> = regex!(";([A-Za-z0-9_\\$]{2,})\\...\\(");
|
||||
|
||||
pub static NSIG_FUNCTION_NAME: &str = "decrypt_nsig";
|
||||
pub static SIG_FUNCTION_NAME: &str = "decrypt_sig";
|
||||
|
56
src/jobs.rs
56
src/jobs.rs
@ -15,6 +15,8 @@ use crate::consts::{NSIG_FUNCTION_ARRAY, NSIG_FUNCTION_NAME, REGEX_PLAYER_ID, TE
|
||||
pub enum JobOpcode {
|
||||
ForceUpdate,
|
||||
DecryptNSignature,
|
||||
DecryptSignature,
|
||||
GetSignatureTimestamp,
|
||||
UnknownOpcode,
|
||||
}
|
||||
|
||||
@ -23,6 +25,8 @@ impl std::fmt::Display for JobOpcode {
|
||||
match self {
|
||||
Self::ForceUpdate => write!(f, "ForceUpdate"),
|
||||
Self::DecryptNSignature => write!(f, "DecryptNSignature"),
|
||||
Self::DecryptSignature => write!(f, "DecryptSignature"),
|
||||
Self::GetSignatureTimestamp => write!(f, "GetSignatureTimestamp"),
|
||||
Self::UnknownOpcode => write!(f, "UnknownOpcode"),
|
||||
}
|
||||
}
|
||||
@ -32,9 +36,8 @@ impl From<u8> for JobOpcode {
|
||||
match value {
|
||||
0x00 => Self::ForceUpdate,
|
||||
0x01 => Self::DecryptNSignature,
|
||||
|
||||
// make debugging easier
|
||||
b'a' => Self::ForceUpdate,
|
||||
0x02 => Self::DecryptSignature,
|
||||
0x03 => Self::GetSignatureTimestamp,
|
||||
_ => Self::UnknownOpcode,
|
||||
}
|
||||
}
|
||||
@ -94,19 +97,40 @@ impl GlobalState {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn process_fetch_update(state: Arc<GlobalState>) {
|
||||
|
||||
macro_rules! write_failure {
|
||||
($s:ident, $r:ident) => {
|
||||
$s.write_u32($r).await;
|
||||
$s.write_u16(0x0000).await;
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn process_fetch_update<W>(
|
||||
state: Arc<GlobalState>,
|
||||
stream: Arc<Mutex<W>>,
|
||||
request_id: u32,
|
||||
) where
|
||||
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||
{
|
||||
let cloned_writer = stream.clone();
|
||||
let mut writer = cloned_writer.lock().await;
|
||||
|
||||
let global_state = state.clone();
|
||||
let response = match reqwest::get(TEST_YOUTUBE_VIDEO).await {
|
||||
Ok(req) => req.text().await.unwrap(),
|
||||
Err(x) => {
|
||||
println!("Could not fetch the test video: {}", x);
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let player_id_str = match REGEX_PLAYER_ID.captures(&response).unwrap().get(1) {
|
||||
Some(result) => result.as_str(),
|
||||
None => return,
|
||||
None => {
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let player_id: u32 = u32::from_str_radix(player_id_str, 16).unwrap();
|
||||
@ -118,6 +142,8 @@ pub async fn process_fetch_update(state: Arc<GlobalState>) {
|
||||
|
||||
if player_id == current_player_id {
|
||||
// Player is already up to date
|
||||
writer.write_u32(request_id).await;
|
||||
writer.write_u16(0xFFFF).await;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -130,6 +156,7 @@ pub async fn process_fetch_update(state: Arc<GlobalState>) {
|
||||
Ok(req) => req.text().await.unwrap(),
|
||||
Err(x) => {
|
||||
println!("Could not fetch the player JS: {}", x);
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -152,6 +179,7 @@ pub async fn process_fetch_update(state: Arc<GlobalState>) {
|
||||
Ok(x) => x,
|
||||
Err(x) => {
|
||||
println!("Error: nsig regex compilation failed: {}", x);
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -186,10 +214,19 @@ pub async fn process_fetch_update(state: Arc<GlobalState>) {
|
||||
.unwrap()
|
||||
.as_str();
|
||||
|
||||
// Extract signature function name
|
||||
|
||||
current_player_info = global_state.player_info.lock().await;
|
||||
current_player_info.player_id = player_id;
|
||||
current_player_info.nsig_function_code = nsig_function_code;
|
||||
println!("Successfully updated the player")
|
||||
|
||||
writer.write_u32(request_id).await;
|
||||
// sync code to tell the client the player had updated
|
||||
writer.write_u16(0xF44F).await;
|
||||
|
||||
writer.flush().await;
|
||||
|
||||
println!("Successfully updated the player");
|
||||
}
|
||||
|
||||
pub async fn process_decrypt_n_signature<W>(
|
||||
@ -200,6 +237,8 @@ pub async fn process_decrypt_n_signature<W>(
|
||||
) where
|
||||
W: tokio::io::AsyncWrite + Unpin + Send,
|
||||
{
|
||||
let cloned_writer = stream.clone();
|
||||
let mut writer = cloned_writer.lock().await;
|
||||
let global_state = state.clone();
|
||||
|
||||
println!("Signature to be decrypted: {}", sig);
|
||||
@ -219,6 +258,7 @@ pub async fn process_decrypt_n_signature<W>(
|
||||
} else {
|
||||
println!("JavaScript interpreter error (nsig code): {}", n);
|
||||
}
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -240,13 +280,11 @@ pub async fn process_decrypt_n_signature<W>(
|
||||
} else {
|
||||
println!("JavaScript interpreter error (nsig code): {}", n);
|
||||
}
|
||||
write_failure!(writer, request_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let cloned_writer = stream.clone();
|
||||
let mut 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;
|
||||
|
@ -86,8 +86,9 @@ async fn process_socket(state: Arc<GlobalState>, socket: UnixStream) -> Result<(
|
||||
match opcode {
|
||||
JobOpcode::ForceUpdate => {
|
||||
let cloned_state = state.clone();
|
||||
let cloned_stream = cloned_writestream.clone();
|
||||
tokio::spawn(async move {
|
||||
process_fetch_update(cloned_state).await;
|
||||
process_fetch_update(cloned_state, cloned_stream, request_id).await;
|
||||
});
|
||||
}
|
||||
JobOpcode::DecryptNSignature => {
|
||||
|
Loading…
Reference in New Issue
Block a user