Skip to content

Commit 57d99bf

Browse files
authored
Relay all messages regardless of order (#17)
Relay all messages regardless of order
1 parent 23bb4fd commit 57d99bf

File tree

2 files changed

+77
-70
lines changed

2 files changed

+77
-70
lines changed

src/main.rs

Lines changed: 45 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,75 +12,64 @@ mod proxy_socket;
1212

1313
use proxy_socket::ProxySocket;
1414

15-
const BUFFER_SIZE: usize = 1024 ^ 2; // 1024 ^ 2 is the maximum
16-
17-
fn valid_length(length: usize) -> bool {
18-
length > 0 && length <= BUFFER_SIZE
19-
}
20-
21-
// Read a header (message size) from stdin (e.g.: from the browser).
22-
fn read_header() -> Result<usize> {
23-
let stdin = stdin();
24-
let mut buf = vec![0; 4];
25-
let mut handle = stdin.lock();
26-
27-
handle.read_exact(&mut buf)?;
15+
// > The maximum size of a single message from the application is 1 MB.
16+
//
17+
// From: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#app_side
18+
const BUFFER_SIZE: usize = 1024 * 1024;
2819

29-
NativeEndian::read_u32(&buf)
30-
.try_into()
31-
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
32-
}
20+
/// Reads from stdin and writes to the socket.
21+
/// Returns on error.
22+
fn stdin_to_socket<T: Read + Write>(socket: &mut ProxySocket<T>) -> Result<()> {
23+
let mut handle = stdin().lock();
24+
let mut len = vec![0; std::mem::size_of::<u32>()];
3325

34-
// Handle a whole request/response cycle
35-
//
36-
// Read a message body from stdin (e.g.: from the browser), and echo it back to the browser's
37-
// socket. Then await a response from the socket and relay that back to the browser.
38-
fn read_body<T: Read + Write>(length: usize, socket: &mut ProxySocket<T>) -> Result<()> {
39-
let mut buffer = vec![0; length];
40-
let stdin = stdin();
41-
let mut handle = stdin.lock();
26+
loop {
27+
handle.read_exact(&mut len)?;
28+
let length: usize = NativeEndian::read_u32(&len)
29+
.try_into()
30+
.map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
4231

43-
handle.read_exact(&mut buffer)?;
32+
let mut buffer = vec![0; length];
33+
handle.read_exact(&mut buffer)?;
4434

45-
if valid_length(length) {
4635
socket.write_all(&buffer)?;
4736
socket.flush()?;
48-
read_response(socket)?;
4937
}
50-
51-
Ok(())
5238
}
5339

54-
// Read a response (from KP's socket) and echo it back to the browser.
55-
fn read_response<T: Read>(socket: &mut ProxySocket<T>) -> Result<()>{
56-
let mut buf = vec![0; BUFFER_SIZE];
57-
if let Ok(len) = socket.read(&mut buf) {
58-
write_response(&buf[0..len])?;
40+
/// Reads from the socket and writes to stdout.
41+
/// Returns on error.
42+
fn socket_to_stdout<T: Read + Write>(socket: &mut ProxySocket<T>) -> Result<()> {
43+
let mut out = stdout().lock();
44+
let mut buf = [0; BUFFER_SIZE];
45+
46+
loop {
47+
if let Ok(len) = socket.read(&mut buf) {
48+
// If a message is larger than the maximum, ignore it entirely. These are disallowed
49+
// by the browser anyway, so sending one would be a protocol violation.
50+
if len <= BUFFER_SIZE {
51+
out.write_u32::<NativeEndian>(len as u32)?;
52+
out.write_all(&buf[..len])?;
53+
out.flush()?;
54+
};
55+
} else {
56+
// TOOD: is the socket is closed, we should try to reconnect.
57+
58+
return Err(Error::from(ErrorKind::BrokenPipe));
59+
}
5960
}
60-
61-
Ok(())
6261
}
6362

64-
// Write a response to stdout (e.g.: to the browser).
65-
fn write_response(buf: &[u8]) -> Result<()> {
66-
let stdout = stdout();
67-
let mut out = stdout.lock();
68-
69-
out.write_u32::<NativeEndian>(buf.len() as u32)?;
70-
out.write_all(buf)?;
71-
out.flush()?;
63+
fn main() -> Result<()> {
64+
let mut socket = proxy_socket::connect(BUFFER_SIZE)?;
65+
let mut socket_clone = socket.try_clone()?;
7266

73-
Ok(())
74-
}
67+
thread::spawn(move || socket_to_stdout(&mut socket_clone).unwrap());
7568

76-
fn main() {
77-
let mut socket = proxy_socket::connect(BUFFER_SIZE).unwrap();
69+
// If stdin is closed, that means that Firefox has exited, so we exit too.
70+
// If the socket is closed, this will (eventually) fail too, however, this can later be
71+
// refactored to reconnect the underlying ProxySocket.
72+
stdin_to_socket(&mut socket).unwrap();
7873

79-
// Start thread for user input reading
80-
let ui = thread::spawn(move || loop {
81-
let length = read_header().unwrap();
82-
read_body(length, &mut socket).unwrap();
83-
});
84-
85-
let _ui_res = ui.join().unwrap();
74+
Ok(())
8675
}

src/proxy_socket.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ use std::env;
22
use std::io::{self, Read, Write};
33

44
#[cfg(not(windows))]
5-
use std::os::unix::io::AsRawFd;
6-
use std::os::unix::net::UnixStream;
7-
use std::path::PathBuf;
8-
use nix::sys::socket;
9-
use nix::sys::socket::sockopt::SndBuf;
10-
use nix::sys::socket::sockopt::RcvBuf;
5+
use {
6+
nix::sys::socket::{
7+
setsockopt,
8+
sockopt::{RcvBuf, SndBuf},
9+
},
10+
std::os::unix::{io::AsRawFd, net::UnixStream},
11+
std::path::PathBuf,
12+
};
1113

1214
#[cfg(windows)]
1315
use named_pipe::PipeClient;
@@ -16,6 +18,20 @@ pub struct ProxySocket<T> {
1618
inner: T,
1719
}
1820

21+
#[cfg(not(windows))]
22+
impl ProxySocket<UnixStream> {
23+
pub(crate) fn try_clone(&self) -> io::Result<Self> {
24+
let inner = self.inner.try_clone()?;
25+
Ok(Self { inner })
26+
}
27+
}
28+
#[cfg(windows)]
29+
impl ProxySocket<PipeClient> {
30+
pub(crate) fn try_clone(&self) -> io::Result<Self> {
31+
todo!();
32+
}
33+
}
34+
1935
impl<R: Read> Read for ProxySocket<R> {
2036
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2137
self.inner.read(buf)
@@ -33,9 +49,12 @@ impl<W: Write> Write for ProxySocket<W> {
3349
}
3450

3551
#[cfg(windows)]
36-
pub fn connect(buffer_size: usize) -> io::Result<ProxySocket<PipeClient>> {
52+
pub fn connect(_buffer_size: usize) -> io::Result<ProxySocket<PipeClient>> {
3753
let username = env::var("USERNAME").unwrap();
38-
let pipe_name = format!("\\\\.\\pipe\\keepassxc\\{}\\org.keepassxc.KeePassXC.BrowserServer", username);
54+
let pipe_name = format!(
55+
"\\\\.\\pipe\\keepassxc\\{}\\org.keepassxc.KeePassXC.BrowserServer",
56+
username
57+
);
3958
let client = PipeClient::connect(pipe_name)?;
4059
Ok(ProxySocket { inner: client })
4160
}
@@ -70,19 +89,18 @@ fn get_socket_dirs() -> Vec<PathBuf> {
7089

7190
#[cfg(not(windows))]
7291
pub fn connect(buffer_size: usize) -> io::Result<ProxySocket<UnixStream>> {
73-
use std::time::Duration;
74-
7592
let socket_name = "org.keepassxc.KeePassXC.BrowserServer";
7693
let dirs = get_socket_dirs();
7794
let s = dirs
7895
.iter()
7996
.find_map(|dir| UnixStream::connect(dir.join(socket_name)).ok())
8097
.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
8198

82-
socket::setsockopt(s.as_raw_fd(), SndBuf, &buffer_size)?;
83-
socket::setsockopt(s.as_raw_fd(), RcvBuf, &buffer_size)?;
99+
setsockopt(s.as_raw_fd(), SndBuf, &buffer_size)?;
100+
setsockopt(s.as_raw_fd(), RcvBuf, &buffer_size)?;
101+
102+
// Make sure reads are blocking.
103+
s.set_nonblocking(false)?;
84104

85-
let timeout: Option<Duration> = Some(Duration::from_secs(1));
86-
s.set_read_timeout(timeout)?;
87105
Ok(ProxySocket { inner: s })
88106
}

0 commit comments

Comments
 (0)