Add initialization of crypto values
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
derive_builder = "0.11"
|
||||||
|
rand = { version = "0.8", features = ["getrandom"] }
|
||||||
|
regex = "1.5"
|
||||||
reqwest = { version = "0.11", features = ["json", "cookies"] }
|
reqwest = { version = "0.11", features = ["json", "cookies"] }
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "vodafone_runner"
|
name = "vodafone_runner"
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
pub mod station;
|
pub mod station;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate derive_builder;
|
||||||
|
|||||||
186
src/station.rs
186
src/station.rs
@@ -1,43 +1,157 @@
|
|||||||
pub mod station {
|
pub mod station {
|
||||||
pub use reqwest::Client;
|
pub use reqwest::Client;
|
||||||
use reqwest::header;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
use derive_builder::UninitializedFieldError;
|
||||||
pub struct VodafoneStation {
|
use rand::{rngs::OsRng, RngCore};
|
||||||
host: String,
|
use regex::Regex;
|
||||||
client: Client,
|
use reqwest::header;
|
||||||
session_id: String,
|
use std::time::Duration;
|
||||||
nonce: String,
|
use thiserror::Error;
|
||||||
csrf_nonce: String,
|
|
||||||
init_vector: String,
|
|
||||||
salt: String,
|
|
||||||
key: String,
|
|
||||||
cookie: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VodafoneStation {
|
#[derive(Debug, Error)]
|
||||||
pub fn new(host: String) -> Self {
|
pub enum StationError {
|
||||||
let mut headers = header::HeaderMap::new();
|
#[error("request error")]
|
||||||
headers.insert("X-Requested-With", header::HeaderValue::from_static("XMLHttpRequest"));
|
Request(#[from] reqwest::Error),
|
||||||
headers.insert(header::REFERER,
|
#[error("could not parse data: {0}")]
|
||||||
header::HeaderValue::from_str(&format!("http://{}/?overview", host))
|
Parse(String),
|
||||||
.expect("Host name is not valid ASCII!"));
|
}
|
||||||
headers.insert(header::ORIGIN,
|
|
||||||
header::HeaderValue::from_str(&format!("http://{}", host))
|
|
||||||
.expect("Host name is not valid ASCII!"));
|
|
||||||
|
|
||||||
let client = Client::builder()
|
#[derive(Debug, Builder)]
|
||||||
.default_headers(headers)
|
#[builder(build_fn(error = "StationError"))]
|
||||||
.user_agent("Mozilla/5.0 (Windows NT 6.1; rv:91.0) Gecko/20100101 Firefox/91.0")
|
struct CryptoValues {
|
||||||
.cookie_store(true)
|
session_id: String,
|
||||||
.timeout(Duration::from_secs(10))
|
nonce: String,
|
||||||
.build().expect("Could not construct reqwest client.");
|
init_vector: String,
|
||||||
|
salt: String,
|
||||||
|
}
|
||||||
|
|
||||||
VodafoneStation {
|
impl From<UninitializedFieldError> for StationError {
|
||||||
host: host,
|
fn from(ufe: UninitializedFieldError) -> StationError {
|
||||||
|
StationError::Parse(ufe.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
#[derive(Debug, Builder)]
|
||||||
}
|
struct LoginState {
|
||||||
}
|
csrf_nonce: String,
|
||||||
|
key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VodafoneStation {
|
||||||
|
host: String,
|
||||||
|
client: Client,
|
||||||
|
crypto: Option<CryptoValues>,
|
||||||
|
state: Option<LoginState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VodafoneStation {
|
||||||
|
pub fn new(host: &str) -> Self {
|
||||||
|
let mut headers = header::HeaderMap::new();
|
||||||
|
headers.insert(
|
||||||
|
"X-Requested-With",
|
||||||
|
header::HeaderValue::from_static("XMLHttpRequest"),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
header::REFERER,
|
||||||
|
header::HeaderValue::from_str(&format!("http://{}/?overview", host))
|
||||||
|
.expect("Host name is not valid ASCII!"),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
header::ORIGIN,
|
||||||
|
header::HeaderValue::from_str(&format!("http://{}", host))
|
||||||
|
.expect("Host name is not valid ASCII!"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = Client::builder()
|
||||||
|
.default_headers(headers)
|
||||||
|
.user_agent("Mozilla/5.0 (Windows NT 6.1; rv:91.0) Gecko/20100101 Firefox/91.0")
|
||||||
|
.cookie_store(true)
|
||||||
|
.timeout(Duration::from_secs(10))
|
||||||
|
.build()
|
||||||
|
.expect("Could not construct reqwest client.");
|
||||||
|
|
||||||
|
VodafoneStation {
|
||||||
|
host: host.to_owned(),
|
||||||
|
client,
|
||||||
|
crypto: None,
|
||||||
|
state: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect(&mut self) -> Result<(), StationError> {
|
||||||
|
let response = self.client.get("/").send().await?;
|
||||||
|
let text = response.text().await?;
|
||||||
|
self.init_crypto(&text)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_crypto(&mut self, response: &str) -> Result<(), StationError> {
|
||||||
|
let re = Regex::new(
|
||||||
|
r"(?x) # Ignore whitespace; allow line comments
|
||||||
|
(\W|^) [[:space:]]* # Start of Javascript statement
|
||||||
|
var [[:space:]]+ (?P<varName>[[:word:]]+) # variable declaration
|
||||||
|
[[:space:]]* = [[:space:]]* # Equals sign
|
||||||
|
'(?P<strval>[^']*)' # String value
|
||||||
|
[[:space:]]*; # End of statement
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut stbuild = CryptoValuesBuilder::default();
|
||||||
|
|
||||||
|
for caps in re.captures_iter(&response) {
|
||||||
|
match &caps["varName"] {
|
||||||
|
"currentSessionId" => {
|
||||||
|
stbuild.session_id(caps["strval"].to_owned());
|
||||||
|
}
|
||||||
|
"myIv" => {
|
||||||
|
stbuild.init_vector(caps["strval"].to_owned());
|
||||||
|
}
|
||||||
|
"mySalt" => {
|
||||||
|
stbuild.salt(caps["strval"].to_owned());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbuild.nonce(OsRng.next_u32().to_string());
|
||||||
|
|
||||||
|
self.crypto = Some(stbuild.build()?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_init_crypto() {
|
||||||
|
let mut station = VodafoneStation::new("INV");
|
||||||
|
let session_id = "cool_session";
|
||||||
|
let iv = "so_initialized";
|
||||||
|
let salt = "extremely_salty";
|
||||||
|
station
|
||||||
|
.init_crypto(
|
||||||
|
format!(
|
||||||
|
r"var currentSessionId = '{}';
|
||||||
|
var myIv = '{}';
|
||||||
|
var mySalt = '{}';
|
||||||
|
",
|
||||||
|
&session_id, &iv, &salt
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
)
|
||||||
|
.expect("Couldn't parse Javascript variable declarations.");
|
||||||
|
|
||||||
|
assert!(station.crypto.is_some());
|
||||||
|
let crypto = station.crypto.unwrap();
|
||||||
|
assert_eq!(crypto.session_id, session_id);
|
||||||
|
assert_eq!(crypto.init_vector, iv);
|
||||||
|
assert_eq!(crypto.salt, salt);
|
||||||
|
assert!(crypto.nonce.len() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user