aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/Devices.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-08-01 00:00:00 +0000
committerLena <lena@omega>2026-08-01 00:00:00 +0000
commit852a8a00273c9128efeed0217a8e5d3fcd8bf780 (patch)
treec72f959731fa3c7c809cf1a0222363b6c9c9b03b /app/src/main/java/invalid/lena/scrcpy/Devices.java
parentf379b93d52bf0dbd3816ec3f64d165314771d2a6 (diff)
downloadscrcpy-android-852a8a00273c9128efeed0217a8e5d3fcd8bf780.tar.gz
app: harden mirroring lifecycle and state
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Devices.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Devices.java86
1 files changed, 74 insertions, 12 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Devices.java b/app/src/main/java/invalid/lena/scrcpy/Devices.java
index 0ba61a3..9ec308c 100644
--- a/app/src/main/java/invalid/lena/scrcpy/Devices.java
+++ b/app/src/main/java/invalid/lena/scrcpy/Devices.java
@@ -5,10 +5,12 @@ import android.content.Context;
import org.json.JSONArray;
import org.json.JSONObject;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
@@ -20,6 +22,8 @@ import java.util.List;
public final class Devices {
private static final String FILE = "devices.json";
+ private static final long MAX_FILE_BYTES = 1024 * 1024;
+ private static final int MAX_HOST_CHARS = 255;
public static final class Device {
public final String host;
@@ -74,7 +78,7 @@ public final class Devices {
port = s.substring(colon + 1);
}
int p = parsePort(port);
- if (host.isEmpty() || p < 0) return null;
+ if (!validHost(host) || p < 0) return null;
return new Device(host, p);
}
@@ -99,13 +103,29 @@ public final class Devices {
json = json.trim();
if (json.isEmpty()) return out;
JSONArray arr;
- try { arr = new JSONArray(json); }
- catch (Exception e) { Log.e(e, "devices: not a json array"); return out; }
+ try {
+ arr = new JSONArray(json);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("devices: expected a JSON array", e);
+ }
for (int i = 0; i < arr.length(); i++) {
try {
JSONObject o = arr.getJSONObject(i);
- out.add(new Device(o.getString("host"), o.getInt("port")));
+ String host = o.getString("host");
+ Object portValue = o.get("port");
+ if (!(portValue instanceof Integer) && !(portValue instanceof Long)) {
+ throw new IllegalArgumentException("port is not an integer");
+ }
+ long portLong = ((Number) portValue).longValue();
+ if (portLong < 1 || portLong > 65535) {
+ throw new IllegalArgumentException("port is invalid");
+ }
+ int port = (int) portLong;
+ if (!validHost(host) || port < 1 || port > 65535) {
+ throw new IllegalArgumentException("invalid device fields");
+ }
+ out.add(new Device(host, port));
} catch (Exception e) {
Log.w("devices: skipping malformed row %d: %s", i, e);
}
@@ -117,6 +137,9 @@ public final class Devices {
try {
JSONArray arr = new JSONArray();
for (Device d : devices) {
+ if (!validHost(d.host) || d.port < 1 || d.port > 65535) {
+ throw new IllegalArgumentException("invalid saved device");
+ }
JSONObject o = new JSONObject();
o.put("host", d.host);
o.put("port", d.port);
@@ -124,23 +147,35 @@ public final class Devices {
}
return arr.toString(2);
} catch (Exception e) {
- Log.e(e, "devices: serialize failed");
- return "[]";
+ throw new IllegalStateException("devices: serialize failed", e);
}
}
- public static List<Device> load(Context ctx) {
+ public static List<Device> load(Context ctx) throws IOException {
File f = new File(ctx.getFilesDir(), FILE);
if (!f.exists()) return new ArrayList<>();
try {
- return parse(new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8));
+ if (f.length() > MAX_FILE_BYTES) {
+ throw new IOException("device list is too large: " + f.length());
+ }
+ byte[] data = readLimited(f);
+ if (data.length == 0) throw new IOException("device list is empty");
+ return parse(Wire.decodeUtf8(data));
} catch (Exception e) {
- Log.e(e, "devices: load failed");
- return new ArrayList<>();
+ if (e instanceof IOException) throw (IOException) e;
+ throw new IOException("devices: load failed", e);
}
}
- public static void save(Context ctx, List<Device> devices) throws IOException {
+ // The saved row for an address, or null if there is none.
+ public static Device find(Context ctx, String host, int port) throws IOException {
+ for (Device d : load(ctx)) {
+ if (d.port == port && d.host.equals(host)) return d;
+ }
+ return null;
+ }
+
+ private static void save(Context ctx, List<Device> devices) throws IOException {
File f = new File(ctx.getFilesDir(), FILE);
AtomicFiles.write(f, serialize(devices).getBytes(StandardCharsets.UTF_8));
}
@@ -162,4 +197,31 @@ public final class Devices {
save(ctx, list);
return list;
}
+
+ private static boolean validHost(String host) {
+ if (host == null || host.isEmpty() || host.length() > MAX_HOST_CHARS) return false;
+ for (int i = 0; i < host.length(); i++) {
+ char c = host.charAt(i);
+ if (Character.isWhitespace(c) || Character.isISOControl(c) || c == '[' || c == ']') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static byte[] readLimited(File file) throws IOException {
+ try (InputStream in = new FileInputStream(file);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ byte[] buf = new byte[8192];
+ long total = 0;
+ for (;;) {
+ int n = in.read(buf);
+ if (n < 0) return out.toByteArray();
+ if (n == 0) throw new IOException("device list read made no progress");
+ total += n;
+ if (total > MAX_FILE_BYTES) throw new IOException("device list is too large");
+ out.write(buf, 0, n);
+ }
+ }
+ }
}