Skip to content

Commit 06d150c

Browse files
committed
Relay all messages regardless of order
The existing implementation expects alternating messages from each source. If either side delivered two consecutive messages, these were not handled properly and communication ended up out-of-sync. This new approach runs two threads, each one handling data in one direction. I replaced the BUFFER_SIZE because the caret (^) symbol means bitwise or in rust, so: 1024 ^ 2 = 1026 The read timeout has been removed, since we now actually WANT to block indefinitely if there is no traffic in a given direction. Fixes: #13 Fixes: #14
1 parent 22c65c0 commit 06d150c

File tree

2 files changed

+52
-62
lines changed

2 files changed

+52
-62
lines changed

src/main.rs

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,75 +12,54 @@ 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+
fn stdin_to_socket<T: Read + Write>(socket: &mut ProxySocket<T>) -> Result<()> {
21+
let mut handle = stdin().lock();
22+
let mut len = vec![0; std::mem::size_of::<u32>()];
3323

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();
24+
loop {
25+
handle.read_exact(&mut len)?;
26+
let length: usize = NativeEndian::read_u32(&len)
27+
.try_into()
28+
.map_err(|err| Error::new(ErrorKind::InvalidData, err))?;
4229

43-
handle.read_exact(&mut buffer)?;
30+
let mut buffer = vec![0; length];
31+
handle.read_exact(&mut buffer)?;
4432

45-
if valid_length(length) {
4633
socket.write_all(&buffer)?;
4734
socket.flush()?;
48-
read_response(socket)?;
4935
}
50-
51-
Ok(())
5236
}
5337

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])?;
38+
/// Reads from the socket and writes to stdout.
39+
/// Returns when the socket is closed.
40+
fn socket_to_stdout<T: Read + Write>(socket: &mut ProxySocket<T>) -> Result<()> {
41+
let mut out = stdout().lock();
42+
let mut buf = [0; BUFFER_SIZE];
43+
44+
while let Ok(len) = socket.read(&mut buf) {
45+
// If a message is larger than the maximum, ignore it entirely. These are disallowed
46+
// by the browser anyway, so sending one would be a protocol violation.
47+
if len <= BUFFER_SIZE {
48+
out.write_u32::<NativeEndian>(len as u32)?;
49+
out.write_all(&buf[..len])?;
50+
out.flush()?;
51+
}
5952
}
6053

6154
Ok(())
6255
}
6356

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();
57+
fn main() -> Result<()> {
58+
let mut socket = proxy_socket::connect(BUFFER_SIZE)?;
59+
let mut socket_clone = socket.try_clone()?;
6860

69-
out.write_u32::<NativeEndian>(buf.len() as u32)?;
70-
out.write_all(buf)?;
71-
out.flush()?;
61+
thread::spawn(move || stdin_to_socket(&mut socket));
62+
socket_to_stdout(&mut socket_clone)?;
7263

7364
Ok(())
7465
}
75-
76-
fn main() {
77-
let mut socket = proxy_socket::connect(BUFFER_SIZE).unwrap();
78-
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();
86-
}

src/proxy_socket.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use std::env;
22
use std::io::{self, Read, Write};
33

4+
use nix::sys::socket;
5+
use nix::sys::socket::sockopt::RcvBuf;
6+
use nix::sys::socket::sockopt::SndBuf;
47
#[cfg(not(windows))]
58
use std::os::unix::io::AsRawFd;
69
use std::os::unix::net::UnixStream;
710
use std::path::PathBuf;
8-
use nix::sys::socket;
9-
use nix::sys::socket::sockopt::SndBuf;
10-
use nix::sys::socket::sockopt::RcvBuf;
1111

1212
#[cfg(windows)]
1313
use named_pipe::PipeClient;
@@ -16,6 +16,15 @@ pub struct ProxySocket<T> {
1616
inner: T,
1717
}
1818

19+
impl ProxySocket<UnixStream> {
20+
pub(crate) fn try_clone(&self) -> io::Result<Self> {
21+
let inner = self.inner.try_clone()?;
22+
Ok(Self { inner })
23+
}
24+
}
25+
26+
// TODO: the window Pipeclient
27+
1928
impl<R: Read> Read for ProxySocket<R> {
2029
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
2130
self.inner.read(buf)
@@ -35,7 +44,10 @@ impl<W: Write> Write for ProxySocket<W> {
3544
#[cfg(windows)]
3645
pub fn connect(buffer_size: usize) -> io::Result<ProxySocket<PipeClient>> {
3746
let username = env::var("USERNAME").unwrap();
38-
let pipe_name = format!("\\\\.\\pipe\\keepassxc\\{}\\org.keepassxc.KeePassXC.BrowserServer", username);
47+
let pipe_name = format!(
48+
"\\\\.\\pipe\\keepassxc\\{}\\org.keepassxc.KeePassXC.BrowserServer",
49+
username
50+
);
3951
let client = PipeClient::connect(pipe_name)?;
4052
Ok(ProxySocket { inner: client })
4153
}
@@ -70,8 +82,6 @@ fn get_socket_dirs() -> Vec<PathBuf> {
7082

7183
#[cfg(not(windows))]
7284
pub fn connect(buffer_size: usize) -> io::Result<ProxySocket<UnixStream>> {
73-
use std::time::Duration;
74-
7585
let socket_name = "org.keepassxc.KeePassXC.BrowserServer";
7686
let dirs = get_socket_dirs();
7787
let s = dirs
@@ -82,7 +92,8 @@ pub fn connect(buffer_size: usize) -> io::Result<ProxySocket<UnixStream>> {
8292
socket::setsockopt(s.as_raw_fd(), SndBuf, &buffer_size)?;
8393
socket::setsockopt(s.as_raw_fd(), RcvBuf, &buffer_size)?;
8494

85-
let timeout: Option<Duration> = Some(Duration::from_secs(1));
86-
s.set_read_timeout(timeout)?;
95+
// Make sure reads are blocking.
96+
s.set_nonblocking(false)?;
97+
8798
Ok(ProxySocket { inner: s })
8899
}

0 commit comments

Comments
 (0)