-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
basti/nativeMessaging #10196
base: main
Are you sure you want to change the base?
basti/nativeMessaging #10196
Changes from all commits
410484d
6dc8179
6f95fc7
b7496b6
a8d8b21
cda6a7e
329f5ec
d32334f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
|
||
|
||
use std::error::Error; | ||
use serde_json::{Value, json}; | ||
|
||
/** | ||
* Handles commands that are sent from | ||
* [Extension] === > [NativeMessagingBridge] | ||
* | ||
* Returns true if the command was handled, in which case it should | ||
* *not* be forwarded to the VPN Client. | ||
* | ||
* Will attempt to print to STDOUT in case a command needs a response. | ||
* | ||
*/ | ||
pub fn handle(val:&Value)-> Result<bool,Box<dyn Error>>{ | ||
let obj = val.as_object().ok_or("Not an object")?; | ||
// Type of command is in {t:'doThing'} | ||
let cmd = obj.get_key_value("t").ok_or("Missing obj.t")?; | ||
|
||
match cmd.1.as_str().ok_or("T is not a string")? { | ||
"bridge_ping" =>{ | ||
crate::io::write_output(std::io::stdout(),&json!({"status": "bridge_pong"})) | ||
.expect("Unable to Write to STDOUT?"); | ||
Ok(true) | ||
} | ||
"start" =>{ | ||
let out = launcher::start_vpn(); | ||
crate::io::write_output(std::io::stdout(),&out) | ||
.expect("Unable to Write to STDOUT?"); | ||
Ok(true) | ||
} | ||
_ =>{ | ||
// We did not handle this. | ||
Ok(false) | ||
} | ||
} | ||
} | ||
|
||
|
||
#[cfg(target_os = "windows")] | ||
mod launcher { | ||
const CLIENT_PATH: &str = "C:\\Program Files\\Mozilla\\Mozilla VPN\\Mozilla VPN.exe"; | ||
|
||
use std::os::windows::process::CommandExt; | ||
use std::process::Command; | ||
|
||
use serde_json::json; | ||
|
||
const CREATE_NEW_PROCESS_GROUP: u32 = 0x200; // CREATE_NEW_PROCESS_GROUP | ||
const DETACHED_PROCESS: u32 = 0x00000008; // DETACHED_PROCESS | ||
|
||
pub fn start_vpn() -> serde_json::Value{ | ||
let result = Command::new(CLIENT_PATH) | ||
.args(["-foreground"]) | ||
.creation_flags(CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS) | ||
.spawn(); | ||
|
||
match result { | ||
Ok(_) => json!("{status:'requested_start'}"), | ||
Err(_) => json!("{error:'start_failed'}"), | ||
} | ||
} | ||
|
||
|
||
} | ||
|
||
#[cfg(not(target_os = "windows"))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After doing a bit of Googling, I think the way to handle this on Linux would be to use a deep-link to try and launch the VPN client. This could then be invoked using the This is not a request to change anything here, we can tackle that in a future PR. |
||
mod launcher { | ||
use serde_json::json; | ||
pub fn start_vpn() -> serde_json::Value{ | ||
json!("{error:'start_unsupported!'}") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; | ||
|
||
|
||
use serde_json::{json, Value}; | ||
use std::io::{Cursor, Read, Write}; | ||
use std::mem::size_of; | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#[derive(PartialEq)] | ||
enum ReaderState { | ||
ReadingLength, | ||
ReadingBuffer, | ||
} | ||
|
||
pub struct Reader { | ||
state: ReaderState, | ||
buffer: Vec<u8>, | ||
length: usize, | ||
} | ||
|
||
impl Reader { | ||
pub fn new() -> Reader { | ||
Reader { | ||
state: ReaderState::ReadingLength, | ||
buffer: Vec::new(), | ||
length: 0, | ||
} | ||
} | ||
|
||
pub fn read_input<R: Read>(&mut self, mut input: R) -> Option<Value> { | ||
// Until we are able to read things from the stream... | ||
loop { | ||
if self.state == ReaderState::ReadingLength { | ||
assert!(self.buffer.len() < size_of::<u32>()); | ||
|
||
let mut buffer = vec![0; size_of::<u32>() - self.buffer.len()]; | ||
match input.read(&mut buffer) { | ||
Ok(size) => { | ||
// Maybe we have read just part of the buffer. Let's append | ||
// only what we have been read. | ||
buffer.truncate(size); | ||
self.buffer.append(&mut buffer); | ||
|
||
// Not enough data yet. | ||
if self.buffer.len() < size_of::<u32>() { | ||
continue; | ||
} | ||
|
||
// Let's convert our buffer into a u32. | ||
let mut rdr = Cursor::new(&self.buffer); | ||
self.length = rdr.read_u32::<NativeEndian>().unwrap() as usize; | ||
if self.length == 0 { | ||
continue; | ||
} | ||
|
||
self.state = ReaderState::ReadingBuffer; | ||
self.buffer = Vec::with_capacity(self.length); | ||
} | ||
_ => return None, | ||
} | ||
} | ||
|
||
if self.state == ReaderState::ReadingBuffer { | ||
assert!(self.length > 0); | ||
assert!(self.buffer.len() < self.length); | ||
|
||
let mut buffer = vec![0; self.length - self.buffer.len()]; | ||
match input.read(&mut buffer) { | ||
Ok(size) => { | ||
// Maybe we have read just part of the buffer. Let's append | ||
// only what we have been read. | ||
buffer.truncate(size); | ||
self.buffer.append(&mut buffer); | ||
|
||
// Not enough data yet. | ||
if self.buffer.len() < self.length { | ||
continue; | ||
} | ||
|
||
match serde_json::from_slice(&self.buffer) { | ||
Ok(value) => { | ||
self.buffer.clear(); | ||
self.state = ReaderState::ReadingLength; | ||
return Some(value); | ||
} | ||
_ => { | ||
self.buffer.clear(); | ||
self.state = ReaderState::ReadingLength; | ||
continue; | ||
} | ||
} | ||
} | ||
_ => return None, | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn write_output<W: Write>(mut output: W, value: &Value) -> Result<(), std::io::Error> { | ||
let msg = serde_json::to_string(value)?; | ||
let len = msg.len(); | ||
output.write_u32::<NativeEndian>(len as u32)?; | ||
output.write_all(msg.as_bytes())?; | ||
output.flush()?; | ||
Ok(()) | ||
} | ||
|
||
pub fn write_vpn_down(error: bool) { | ||
let field = if error { "error" } else { "status" }; | ||
let value = json!({field: "vpn-client-down"}); | ||
write_output(std::io::stdout(), &value).expect("Unable to write to STDOUT"); | ||
} | ||
|
||
pub fn write_vpn_up() { | ||
let value = json!({"status": "vpn-client-up"}); | ||
write_output(std::io::stdout(), &value).expect("Unable to write to STDOUT"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any way it's ever installed to a different directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we don't offer other ways of installation, no :)