From 4315be164823d2c8fc44b475b52af79bfee98ff1 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Fri, 27 Jan 2023 21:48:54 +0100 Subject: [PATCH] Use random name for device socket For the initial connection between the device and the computer, an adb tunnel is established (with "adb reverse" or "adb forward"). The device-side of the tunnel is a local socket having the hard-coded name "scrcpy". This may cause issues when several scrcpy instances are started in a few seconds for the same device, since they will try to bind the same name. To avoid conflicts, make the client generate a random UID, and append this UID to the local socket name ("scrcpy_01234567"). --- app/src/adb/adb_tunnel.c | 24 ++++++++-------- app/src/adb/adb_tunnel.h | 6 ++-- app/src/scrcpy.c | 12 ++++++++ app/src/server.c | 28 +++++++++++++++---- app/src/server.h | 2 ++ .../genymobile/scrcpy/DesktopConnection.java | 21 ++++++++++---- .../java/com/genymobile/scrcpy/Options.java | 9 ++++++ .../java/com/genymobile/scrcpy/Server.java | 10 ++++++- 8 files changed, 87 insertions(+), 25 deletions(-) diff --git a/app/src/adb/adb_tunnel.c b/app/src/adb/adb_tunnel.c index c613bc2b..fa936e4b 100644 --- a/app/src/adb/adb_tunnel.c +++ b/app/src/adb/adb_tunnel.c @@ -7,8 +7,6 @@ #include "util/net_intr.h" #include "util/process_intr.h" -#define SC_SOCKET_NAME "scrcpy" - static bool listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { return net_listen_intr(intr, socket, IPV4_LOCALHOST, port, 1); @@ -17,10 +15,11 @@ listen_on_port(struct sc_intr *intr, sc_socket socket, uint16_t port) { static bool enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, const char *serial, + const char *device_socket_name, struct sc_port_range port_range) { uint16_t port = port_range.first; for (;;) { - if (!sc_adb_reverse(intr, serial, SC_SOCKET_NAME, port, + if (!sc_adb_reverse(intr, serial, device_socket_name, port, SC_ADB_NO_STDOUT)) { // the command itself failed, it will fail on any port return false; @@ -52,7 +51,7 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel, } // failure, disable tunnel and try another port - if (!sc_adb_reverse_remove(intr, serial, SC_SOCKET_NAME, + if (!sc_adb_reverse_remove(intr, serial, device_socket_name, SC_ADB_NO_STDOUT)) { LOGW("Could not remove reverse tunnel on port %" PRIu16, port); } @@ -78,12 +77,13 @@ enable_tunnel_reverse_any_port(struct sc_adb_tunnel *tunnel, static bool enable_tunnel_forward_any_port(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, const char *serial, + const char *device_socket_name, struct sc_port_range port_range) { tunnel->forward = true; uint16_t port = port_range.first; for (;;) { - if (sc_adb_forward(intr, serial, port, SC_SOCKET_NAME, + if (sc_adb_forward(intr, serial, port, device_socket_name, SC_ADB_NO_STDOUT)) { // success tunnel->local_port = port; @@ -123,13 +123,14 @@ sc_adb_tunnel_init(struct sc_adb_tunnel *tunnel) { bool sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, - const char *serial, struct sc_port_range port_range, - bool force_adb_forward) { + const char *serial, const char *device_socket_name, + struct sc_port_range port_range, bool force_adb_forward) { assert(!tunnel->enabled); if (!force_adb_forward) { // Attempt to use "adb reverse" - if (enable_tunnel_reverse_any_port(tunnel, intr, serial, port_range)) { + if (enable_tunnel_reverse_any_port(tunnel, intr, serial, + device_socket_name, port_range)) { return true; } @@ -139,12 +140,13 @@ sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, LOGW("'adb reverse' failed, fallback to 'adb forward'"); } - return enable_tunnel_forward_any_port(tunnel, intr, serial, port_range); + return enable_tunnel_forward_any_port(tunnel, intr, serial, + device_socket_name, port_range); } bool sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, - const char *serial) { + const char *serial, const char *device_socket_name) { assert(tunnel->enabled); bool ret; @@ -152,7 +154,7 @@ sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, ret = sc_adb_forward_remove(intr, serial, tunnel->local_port, SC_ADB_NO_STDOUT); } else { - ret = sc_adb_reverse_remove(intr, serial, SC_SOCKET_NAME, + ret = sc_adb_reverse_remove(intr, serial, device_socket_name, SC_ADB_NO_STDOUT); assert(tunnel->server_socket != SC_SOCKET_NONE); diff --git a/app/src/adb/adb_tunnel.h b/app/src/adb/adb_tunnel.h index 12e3cf17..7ed5bf54 100644 --- a/app/src/adb/adb_tunnel.h +++ b/app/src/adb/adb_tunnel.h @@ -34,14 +34,14 @@ sc_adb_tunnel_init(struct sc_adb_tunnel *tunnel); */ bool sc_adb_tunnel_open(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, - const char *serial, struct sc_port_range port_range, - bool force_adb_forward); + const char *serial, const char *device_socket_name, + struct sc_port_range port_range, bool force_adb_forward); /** * Close the tunnel */ bool sc_adb_tunnel_close(struct sc_adb_tunnel *tunnel, struct sc_intr *intr, - const char *serial); + const char *serial, const char *device_socket_name); #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 3588e9ae..14688471 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -32,6 +32,7 @@ #include "util/acksync.h" #include "util/log.h" #include "util/net.h" +#include "util/rand.h" #ifdef HAVE_V4L2 # include "v4l2_sink.h" #endif @@ -265,6 +266,14 @@ sc_server_on_disconnected(struct sc_server *server, void *userdata) { // event } +static uint32_t +scrcpy_generate_uid() { + struct sc_rand rand; + sc_rand_init(&rand); + // Only use 31 bits to avoid issues with signed values on the Java-side + return sc_rand_u32(&rand) & 0x7FFFFFFF; +} + enum scrcpy_exit_code scrcpy(struct scrcpy_options *options) { static struct scrcpy scrcpy; @@ -298,7 +307,10 @@ scrcpy(struct scrcpy_options *options) { struct sc_acksync *acksync = NULL; + uint32_t uid = scrcpy_generate_uid(); + struct sc_server_params params = { + .uid = uid, .req_serial = options->serial, .select_usb = options->select_usb, .select_tcpip = options->select_tcpip, diff --git a/app/src/server.c b/app/src/server.c index e5970487..9384ce64 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -20,6 +20,7 @@ #define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar" #define SC_ADB_PORT_DEFAULT 5555 +#define SC_SOCKET_NAME_PREFIX "scrcpy_" static char * get_server_path(void) { @@ -197,6 +198,7 @@ execute_server(struct sc_server *server, cmd[count++] = p; \ } + ADD_PARAM("uid=%08x", params->uid); ADD_PARAM("log_level=%s", log_level_to_server_string(params->log_level)); ADD_PARAM("bit_rate=%" PRIu32, params->bit_rate); @@ -364,6 +366,7 @@ sc_server_init(struct sc_server *server, const struct sc_server_params *params, } server->serial = NULL; + server->device_socket_name = NULL; server->stopped = false; server->video_socket = SC_SOCKET_NONE; @@ -463,7 +466,8 @@ sc_server_connect_to(struct sc_server *server, struct sc_server_info *info) { } // we don't need the adb tunnel anymore - sc_adb_tunnel_close(tunnel, &server->intr, serial); + sc_adb_tunnel_close(tunnel, &server->intr, serial, + server->device_socket_name); // The sockets will be closed on stop if device_read_info() fails bool ok = device_read_info(&server->intr, video_socket, info); @@ -494,7 +498,8 @@ fail: if (tunnel->enabled) { // Always leave this function with tunnel disabled - sc_adb_tunnel_close(tunnel, &server->intr, serial); + sc_adb_tunnel_close(tunnel, &server->intr, serial, + server->device_socket_name); } return false; @@ -764,13 +769,23 @@ run_server(void *data) { assert(serial); LOGD("Device serial: %s", serial); + int r = asprintf(&server->device_socket_name, SC_SOCKET_NAME_PREFIX "%08x", + params->uid); + if (r == -1) { + LOG_OOM(); + goto error_connection_failed; + } + assert(r == sizeof(SC_SOCKET_NAME_PREFIX) - 1 + 8); + assert(server->device_socket_name); + ok = push_server(&server->intr, serial); if (!ok) { goto error_connection_failed; } ok = sc_adb_tunnel_open(&server->tunnel, &server->intr, serial, - params->port_range, params->force_adb_forward); + server->device_socket_name, params->port_range, + params->force_adb_forward); if (!ok) { goto error_connection_failed; } @@ -778,7 +793,8 @@ run_server(void *data) { // server will connect to our server socket sc_pid pid = execute_server(server, params); if (pid == SC_PROCESS_NONE) { - sc_adb_tunnel_close(&server->tunnel, &server->intr, serial); + sc_adb_tunnel_close(&server->tunnel, &server->intr, serial, + server->device_socket_name); goto error_connection_failed; } @@ -790,7 +806,8 @@ run_server(void *data) { if (!ok) { sc_process_terminate(pid); sc_process_wait(pid, true); // ignore exit code - sc_adb_tunnel_close(&server->tunnel, &server->intr, serial); + sc_adb_tunnel_close(&server->tunnel, &server->intr, serial, + server->device_socket_name); goto error_connection_failed; } @@ -884,6 +901,7 @@ sc_server_destroy(struct sc_server *server) { } free(server->serial); + free(server->device_socket_name); sc_server_params_destroy(&server->params); sc_intr_destroy(&server->intr); sc_cond_destroy(&server->cond_stopped); diff --git a/app/src/server.h b/app/src/server.h index 49ba83c1..e0f2c225 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -22,6 +22,7 @@ struct sc_server_info { }; struct sc_server_params { + uint32_t uid; const char *req_serial; enum sc_log_level log_level; const char *crop; @@ -54,6 +55,7 @@ struct sc_server { // The internal allocated strings are copies owned by the server struct sc_server_params params; char *serial; + char *device_socket_name; sc_thread thread; struct sc_server_info info; // initialized once connected diff --git a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java index 78728d81..54a40d22 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java +++ b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java @@ -15,7 +15,7 @@ public final class DesktopConnection implements Closeable { private static final int DEVICE_NAME_FIELD_LENGTH = 64; - private static final String SOCKET_NAME = "scrcpy"; + private static final String SOCKET_NAME_PREFIX = "scrcpy"; private final LocalSocket videoSocket; private final FileDescriptor videoFd; @@ -46,11 +46,22 @@ public final class DesktopConnection implements Closeable { return localSocket; } - public static DesktopConnection open(boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException { + private static String getSocketName(int uid) { + if (uid == -1) { + // If no UID is set, use "scrcpy" to simplify using scrcpy-server alone + return SOCKET_NAME_PREFIX; + } + + return SOCKET_NAME_PREFIX + String.format("_%08x", uid); + } + + public static DesktopConnection open(int uid, boolean tunnelForward, boolean control, boolean sendDummyByte) throws IOException { + String socketName = getSocketName(uid); + LocalSocket videoSocket; LocalSocket controlSocket = null; if (tunnelForward) { - LocalServerSocket localServerSocket = new LocalServerSocket(SOCKET_NAME); + LocalServerSocket localServerSocket = new LocalServerSocket(socketName); try { videoSocket = localServerSocket.accept(); if (sendDummyByte) { @@ -69,10 +80,10 @@ public final class DesktopConnection implements Closeable { localServerSocket.close(); } } else { - videoSocket = connect(SOCKET_NAME); + videoSocket = connect(socketName); if (control) { try { - controlSocket = connect(SOCKET_NAME); + controlSocket = connect(socketName); } catch (IOException | RuntimeException e) { videoSocket.close(); throw e; diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index d1607c20..171d6661 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -6,6 +6,7 @@ import java.util.List; public class Options { private Ln.Level logLevel = Ln.Level.DEBUG; + private int uid = -1; // 31-bit non-negative value, or -1 private int maxSize; private int bitRate = 8000000; private int maxFps; @@ -37,6 +38,14 @@ public class Options { this.logLevel = logLevel; } + public int getUid() { + return uid; + } + + public void setUid(int uid) { + this.uid = uid; + } + public int getMaxSize() { return maxSize; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index ec03515e..a8b948c6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -66,11 +66,12 @@ public final class Server { Thread initThread = startInitThread(options); + int uid = options.getUid(); boolean tunnelForward = options.isTunnelForward(); boolean control = options.getControl(); boolean sendDummyByte = options.getSendDummyByte(); - try (DesktopConnection connection = DesktopConnection.open(tunnelForward, control, sendDummyByte)) { + try (DesktopConnection connection = DesktopConnection.open(uid, tunnelForward, control, sendDummyByte)) { if (options.getSendDeviceMeta()) { Size videoSize = device.getScreenInfo().getVideoSize(); connection.sendDeviceMeta(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight()); @@ -178,6 +179,13 @@ public final class Server { String key = arg.substring(0, equalIndex); String value = arg.substring(equalIndex + 1); switch (key) { + case "uid": + int uid = Integer.parseInt(value, 0x10); + if (uid < -1) { + throw new IllegalArgumentException("uid may not be negative (except -1 for 'none'): " + uid); + } + options.setUid(uid); + break; case "log_level": Ln.Level level = Ln.Level.valueOf(value.toUpperCase(Locale.ENGLISH)); options.setLogLevel(level);