Skip to content

Commit 6a6523d

Browse files
committed
Replace RandomAccessFile with AsynchronousFileChannel to fix deadlock problems on Windows
1 parent 70b5637 commit 6a6523d

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

src/main/java/org/keepassxc/WindowsConnection.java

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,28 @@
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
88

9-
import java.io.FileNotFoundException;
109
import java.io.IOException;
11-
import java.io.RandomAccessFile;
10+
import java.nio.ByteBuffer;
11+
import java.nio.CharBuffer;
12+
import java.nio.channels.AsynchronousFileChannel;
13+
import java.nio.charset.Charset;
14+
import java.nio.charset.CharsetDecoder;
1215
import java.nio.charset.StandardCharsets;
16+
import java.nio.file.Path;
17+
import java.nio.file.Paths;
18+
import java.nio.file.StandardOpenOption;
19+
import java.util.concurrent.Future;
1320

1421
public class WindowsConnection extends Connection {
1522

1623
private static final Logger log = LoggerFactory.getLogger(WindowsConnection.class);
1724

18-
private RandomAccessFile pipe;
25+
private final int BUFFER_SIZE = 8192;
26+
private AsynchronousFileChannel pipe;
27+
private final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
28+
private final Charset charset = StandardCharsets.UTF_8;
29+
private final CharsetDecoder charsetDecoder = charset.newDecoder();
30+
private final CharBuffer charBuffer = CharBuffer.allocate(BUFFER_SIZE);
1931

2032
/**
2133
* Connect to the KeePassXC proxy via a Windows named pipe the proxy has opened.
@@ -25,9 +37,9 @@ public class WindowsConnection extends Connection {
2537
@Override
2638
public void connect() throws IOException {
2739
try {
28-
pipe = new RandomAccessFile("\\\\.\\pipe\\" + PROXY_NAME + "_" + System.getenv("USERNAME"),
29-
"rw");
30-
} catch (FileNotFoundException e) {
40+
Path path = Paths.get("\\\\.\\pipe\\" + PROXY_NAME + "_" + System.getenv("USERNAME"));
41+
pipe = AsynchronousFileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE);
42+
} catch (IOException e) {
3143
log.error("Cannot connect to proxy. Is KeepassXC started?");
3244
throw e;
3345
}
@@ -42,25 +54,27 @@ public void connect() throws IOException {
4254
@Override
4355
protected void sendCleartextMessage(String msg) throws IOException {
4456
log.trace("Sending message: {}", msg);
45-
pipe.write(msg.getBytes(StandardCharsets.UTF_8));
57+
pipe.write(ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)), 0);
4658
}
4759

4860
@Override
4961
protected JSONObject getCleartextResponse() {
50-
int c;
51-
var raw = "";
52-
do {
53-
try {
54-
c = pipe.read();
55-
raw += (char) c;
56-
} catch (IOException e) {
57-
log.error(e.toString(), e.getCause());
58-
return new JSONObject();
59-
}
60-
} while (c != 125); // end of transmission
62+
var raw = new StringBuilder();
63+
long position = 0;
64+
Future<Integer> operation = pipe.read(buffer, position);
65+
while (!operation.isDone());
66+
buffer.flip();
67+
charsetDecoder.decode(buffer, charBuffer, true);
68+
charBuffer.flip();
69+
raw.append(charBuffer);
70+
buffer.compact();
71+
charBuffer.clear();
6172
log.trace("Reading message: {}", raw);
6273
try {
63-
return new JSONObject(raw);
74+
var s = raw.toString();
75+
// Test, if we received more than one message with the last read
76+
if (s.length() - s.replace("}", "").length() > 1) throw new JSONException("");
77+
return new JSONObject(raw.toString());
6478
} catch (JSONException e) {
6579
log.error("Message corrupted. Received: {}", raw);
6680
return new JSONObject();

0 commit comments

Comments
 (0)