2018-02-08 22:16:27 +08:00
|
|
|
#include "server.h"
|
2018-01-22 18:22:31 +08:00
|
|
|
|
|
|
|
#include <errno.h>
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
#include <inttypes.h>
|
2018-02-01 23:36:50 +08:00
|
|
|
#include <stdint.h>
|
2018-05-13 21:33:13 +08:00
|
|
|
#include <stdio.h>
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
#include <SDL2/SDL_assert.h>
|
2018-03-12 15:35:51 +08:00
|
|
|
#include <SDL2/SDL_timer.h>
|
2018-02-13 17:10:18 +08:00
|
|
|
|
2018-02-13 18:55:12 +08:00
|
|
|
#include "config.h"
|
2018-02-13 17:10:18 +08:00
|
|
|
#include "log.h"
|
2018-02-16 18:11:07 +08:00
|
|
|
#include "net.h"
|
2018-01-22 18:22:31 +08:00
|
|
|
|
2018-01-23 22:46:34 +08:00
|
|
|
#define SOCKET_NAME "scrcpy"
|
|
|
|
|
2018-02-16 22:19:35 +08:00
|
|
|
#ifdef OVERRIDE_SERVER_PATH
|
|
|
|
# define DEFAULT_SERVER_PATH OVERRIDE_SERVER_PATH
|
2018-02-13 18:55:12 +08:00
|
|
|
#else
|
2018-02-16 22:19:35 +08:00
|
|
|
# define DEFAULT_SERVER_PATH PREFIX PREFIXED_SERVER_PATH
|
2018-02-13 18:55:12 +08:00
|
|
|
#endif
|
|
|
|
|
2018-02-28 22:13:56 +08:00
|
|
|
#define DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
|
|
|
|
2018-02-13 18:55:12 +08:00
|
|
|
static const char *get_server_path(void) {
|
2018-02-16 22:19:35 +08:00
|
|
|
const char *server_path = getenv("SCRCPY_SERVER_PATH");
|
2018-02-02 16:31:44 +08:00
|
|
|
if (!server_path) {
|
2018-02-16 22:19:35 +08:00
|
|
|
server_path = DEFAULT_SERVER_PATH;
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
2018-02-13 18:55:12 +08:00
|
|
|
return server_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SDL_bool push_server(const char *serial) {
|
2018-02-28 22:13:56 +08:00
|
|
|
process_t process = adb_push(serial, get_server_path(), DEVICE_SERVER_PATH);
|
2018-02-08 22:16:27 +08:00
|
|
|
return process_check_success(process, "adb push");
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
|
|
|
|
2018-02-28 22:13:56 +08:00
|
|
|
static SDL_bool remove_server(const char *serial) {
|
|
|
|
process_t process = adb_remove_path(serial, DEVICE_SERVER_PATH);
|
|
|
|
return process_check_success(process, "adb shell rm");
|
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
static SDL_bool enable_tunnel_reverse(const char *serial, Uint16 local_port) {
|
2018-02-08 22:16:27 +08:00
|
|
|
process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
|
|
|
|
return process_check_success(process, "adb reverse");
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
static SDL_bool disable_tunnel_reverse(const char *serial) {
|
2018-02-08 22:16:27 +08:00
|
|
|
process_t process = adb_reverse_remove(serial, SOCKET_NAME);
|
|
|
|
return process_check_success(process, "adb reverse --remove");
|
2018-01-23 22:46:34 +08:00
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
static SDL_bool enable_tunnel_forward(const char *serial, Uint16 local_port) {
|
|
|
|
process_t process = adb_forward(serial, local_port, SOCKET_NAME);
|
|
|
|
return process_check_success(process, "adb forward");
|
|
|
|
}
|
|
|
|
|
|
|
|
static SDL_bool disable_tunnel_forward(const char *serial, Uint16 local_port) {
|
|
|
|
process_t process = adb_forward_remove(serial, local_port);
|
|
|
|
return process_check_success(process, "adb forward --remove");
|
|
|
|
}
|
|
|
|
|
|
|
|
static SDL_bool enable_tunnel(struct server *server) {
|
|
|
|
if (enable_tunnel_reverse(server->serial, server->local_port)) {
|
|
|
|
return SDL_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGW("'adb reverse' failed, fallback to 'adb forward'");
|
|
|
|
server->tunnel_forward = SDL_TRUE;
|
|
|
|
return enable_tunnel_forward(server->serial, server->local_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SDL_bool disable_tunnel(struct server *server) {
|
|
|
|
if (server->tunnel_forward) {
|
|
|
|
return disable_tunnel_forward(server->serial, server->local_port);
|
|
|
|
}
|
|
|
|
return disable_tunnel_reverse(server->serial);
|
|
|
|
}
|
|
|
|
|
|
|
|
static process_t execute_server(const char *serial,
|
2018-08-10 01:12:27 +08:00
|
|
|
Uint16 max_size, Uint32 bit_rate,
|
|
|
|
const char *crop, SDL_bool tunnel_forward) {
|
2018-02-01 19:18:06 +08:00
|
|
|
char max_size_string[6];
|
2018-02-01 23:36:50 +08:00
|
|
|
char bit_rate_string[11];
|
|
|
|
sprintf(max_size_string, "%"PRIu16, max_size);
|
|
|
|
sprintf(bit_rate_string, "%"PRIu32, bit_rate);
|
2018-01-22 18:22:31 +08:00
|
|
|
const char *const cmd[] = {
|
|
|
|
"shell",
|
2018-02-02 16:31:44 +08:00
|
|
|
"CLASSPATH=/data/local/tmp/scrcpy-server.jar",
|
2018-01-22 18:22:31 +08:00
|
|
|
"app_process",
|
2018-01-30 00:06:44 +08:00
|
|
|
"/", // unused
|
2018-02-28 21:57:18 +08:00
|
|
|
"com.genymobile.scrcpy.Server",
|
2018-02-01 19:18:06 +08:00
|
|
|
max_size_string,
|
2018-02-01 23:36:50 +08:00
|
|
|
bit_rate_string,
|
2018-03-12 15:35:51 +08:00
|
|
|
tunnel_forward ? "true" : "false",
|
2018-08-10 01:12:27 +08:00
|
|
|
crop ? crop : "",
|
2018-01-22 18:22:31 +08:00
|
|
|
};
|
|
|
|
return adb_execute(serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
|
|
|
|
}
|
|
|
|
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
#define IPV4_LOCALHOST 0x7F000001
|
2018-03-12 15:35:51 +08:00
|
|
|
|
|
|
|
static socket_t listen_on_port(Uint16 port) {
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
return net_listen(IPV4_LOCALHOST, port, 1);
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
static socket_t connect_and_read_byte(Uint16 port) {
|
|
|
|
socket_t socket = net_connect(IPV4_LOCALHOST, port);
|
|
|
|
if (socket == INVALID_SOCKET) {
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
|
|
|
char byte;
|
|
|
|
// the connection may succeed even if the server behind the "adb tunnel"
|
|
|
|
// is not listening, so read one byte to detect a working connection
|
|
|
|
if (net_recv_all(socket, &byte, 1) != 1) {
|
|
|
|
// the server is not listening yet behind the adb tunnel
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
static socket_t connect_to_server(Uint16 port, Uint32 attempts, Uint32 delay) {
|
|
|
|
do {
|
|
|
|
LOGD("Remaining connection attempts: %d", (int) attempts);
|
|
|
|
socket_t socket = connect_and_read_byte(port);
|
|
|
|
if (socket != INVALID_SOCKET) {
|
|
|
|
// it worked!
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
if (attempts) {
|
|
|
|
SDL_Delay(delay);
|
|
|
|
}
|
|
|
|
} while (--attempts > 0);
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
2018-02-16 21:55:33 +08:00
|
|
|
static void close_socket(socket_t *socket) {
|
|
|
|
SDL_assert(*socket != INVALID_SOCKET);
|
2018-03-05 22:09:26 +08:00
|
|
|
net_shutdown(*socket, SHUT_RDWR);
|
2018-02-16 21:55:33 +08:00
|
|
|
if (!net_close(*socket)) {
|
|
|
|
LOGW("Cannot close socket");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*socket = INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
2018-02-08 22:16:27 +08:00
|
|
|
void server_init(struct server *server) {
|
|
|
|
*server = (struct server) SERVER_INITIALIZER;
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_bool server_start(struct server *server, const char *serial, Uint16 local_port,
|
2018-08-10 01:12:27 +08:00
|
|
|
Uint16 max_size, Uint32 bit_rate, const char *crop) {
|
2018-03-12 15:35:51 +08:00
|
|
|
server->local_port = local_port;
|
|
|
|
|
2018-03-12 17:19:12 +08:00
|
|
|
if (serial) {
|
|
|
|
server->serial = SDL_strdup(serial);
|
2018-05-26 20:20:05 +08:00
|
|
|
if (!server->serial) {
|
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
2018-03-12 17:19:12 +08:00
|
|
|
}
|
|
|
|
|
2018-02-08 22:16:27 +08:00
|
|
|
if (!push_server(serial)) {
|
2018-05-26 20:20:05 +08:00
|
|
|
SDL_free((void *) server->serial);
|
2018-02-08 22:16:27 +08:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-02-28 22:13:56 +08:00
|
|
|
server->server_copied_to_device = SDL_TRUE;
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
if (!enable_tunnel(server)) {
|
2018-05-26 20:20:05 +08:00
|
|
|
SDL_free((void *) server->serial);
|
2018-02-08 22:16:27 +08:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
// if "adb reverse" does not work (e.g. over "adb connect"), it fallbacks to
|
|
|
|
// "adb forward", so the app socket is the client
|
|
|
|
if (!server->tunnel_forward) {
|
|
|
|
// At the application level, the device part is "the server" because it
|
|
|
|
// serves video stream and control. However, at the network level, the
|
|
|
|
// client listens and the server connects to the client. That way, the
|
|
|
|
// client can listen before starting the server app, so there is no need to
|
|
|
|
// try to connect until the server socket is listening on the device.
|
|
|
|
|
|
|
|
server->server_socket = listen_on_port(local_port);
|
|
|
|
if (server->server_socket == INVALID_SOCKET) {
|
|
|
|
LOGE("Could not listen on port %" PRIu16, local_port);
|
|
|
|
disable_tunnel(server);
|
2018-05-26 20:20:05 +08:00
|
|
|
SDL_free((void *) server->serial);
|
2018-03-12 15:35:51 +08:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// server will connect to our server socket
|
2018-08-10 01:12:27 +08:00
|
|
|
server->process = execute_server(serial, max_size, bit_rate, crop,
|
|
|
|
server->tunnel_forward);
|
2018-02-08 22:16:27 +08:00
|
|
|
if (server->process == PROCESS_NONE) {
|
2018-03-12 15:35:51 +08:00
|
|
|
if (!server->tunnel_forward) {
|
|
|
|
close_socket(&server->server_socket);
|
|
|
|
}
|
|
|
|
disable_tunnel(server);
|
2018-05-26 20:20:05 +08:00
|
|
|
SDL_free((void *) server->serial);
|
2018-02-08 22:16:27 +08:00
|
|
|
return SDL_FALSE;
|
|
|
|
}
|
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
server->tunnel_enabled = SDL_TRUE;
|
2018-02-08 22:16:27 +08:00
|
|
|
|
|
|
|
return SDL_TRUE;
|
|
|
|
}
|
|
|
|
|
2018-03-22 04:43:12 +08:00
|
|
|
socket_t server_connect_to(struct server *server) {
|
2018-03-12 15:35:51 +08:00
|
|
|
if (!server->tunnel_forward) {
|
|
|
|
server->device_socket = net_accept(server->server_socket);
|
|
|
|
} else {
|
2018-08-10 00:22:42 +08:00
|
|
|
Uint32 attempts = 100;
|
2018-03-12 15:35:51 +08:00
|
|
|
Uint32 delay = 100; // ms
|
|
|
|
server->device_socket = connect_to_server(server->local_port, attempts, delay);
|
|
|
|
}
|
|
|
|
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
if (server->device_socket == INVALID_SOCKET) {
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
2018-02-09 00:38:38 +08:00
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
if (!server->tunnel_forward) {
|
|
|
|
// we don't need the server socket anymore
|
|
|
|
close_socket(&server->server_socket);
|
|
|
|
}
|
2018-02-09 00:38:38 +08:00
|
|
|
|
2018-02-28 22:13:56 +08:00
|
|
|
// the server is started, we can clean up the jar from the temporary folder
|
2018-03-12 17:19:12 +08:00
|
|
|
remove_server(server->serial); // ignore failure
|
2018-02-28 22:13:56 +08:00
|
|
|
server->server_copied_to_device = SDL_FALSE;
|
|
|
|
|
2018-02-09 00:38:38 +08:00
|
|
|
// we don't need the adb tunnel anymore
|
2018-03-12 15:35:51 +08:00
|
|
|
disable_tunnel(server); // ignore failure
|
|
|
|
server->tunnel_enabled = SDL_FALSE;
|
2018-02-09 00:38:38 +08:00
|
|
|
|
2018-02-08 22:16:27 +08:00
|
|
|
return server->device_socket;
|
|
|
|
}
|
|
|
|
|
2018-03-12 17:19:12 +08:00
|
|
|
void server_stop(struct server *server) {
|
2018-02-08 22:16:27 +08:00
|
|
|
SDL_assert(server->process != PROCESS_NONE);
|
2018-02-16 18:11:07 +08:00
|
|
|
|
2018-02-28 23:46:03 +08:00
|
|
|
if (!cmd_terminate(server->process)) {
|
|
|
|
LOGW("Cannot terminate server");
|
2018-02-16 18:11:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cmd_simple_wait(server->process, NULL); // ignore exit code
|
|
|
|
LOGD("Server terminated");
|
2018-02-08 22:16:27 +08:00
|
|
|
|
2018-03-12 15:35:51 +08:00
|
|
|
if (server->tunnel_enabled) {
|
2018-02-08 22:16:27 +08:00
|
|
|
// ignore failure
|
2018-03-12 15:35:51 +08:00
|
|
|
disable_tunnel(server);
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
2018-02-28 22:13:56 +08:00
|
|
|
|
|
|
|
if (server->server_copied_to_device) {
|
2018-03-12 17:19:12 +08:00
|
|
|
remove_server(server->serial); // ignore failure
|
2018-02-28 22:13:56 +08:00
|
|
|
}
|
2018-02-08 22:16:27 +08:00
|
|
|
}
|
2018-02-09 19:59:36 +08:00
|
|
|
|
|
|
|
void server_destroy(struct server *server) {
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
if (server->server_socket != INVALID_SOCKET) {
|
2018-02-16 21:55:33 +08:00
|
|
|
close_socket(&server->server_socket);
|
2018-02-09 19:59:36 +08:00
|
|
|
}
|
Replace SDL_net by custom implementation
SDL_net is not very suitable for scrcpy.
For example, SDLNet_TCP_Accept() is non-blocking, so we have to wrap it
by calling many SDL_Net-specific functions to make it blocking.
But above all, SDLNet_TCP_Open() is a server socket only when no IP is
provided; otherwise, it's a client socket. Therefore, it is not possible
to create a server socket bound to localhost, so it accepts connections
from anywhere.
This is a problem for scrcpy, because on start, the application listens
for nearly 1 second until it accepts the first connection, supposedly
from the device. If someone on the local network manages to connect to
the server socket first, then they can stream arbitrary H.264 video.
This may be troublesome, for example during a public presentation ;-)
Provide our own simplified API (net.h) instead, implemented for the
different platforms.
2018-02-16 05:59:21 +08:00
|
|
|
if (server->device_socket != INVALID_SOCKET) {
|
2018-02-16 21:55:33 +08:00
|
|
|
close_socket(&server->device_socket);
|
2018-02-09 19:59:36 +08:00
|
|
|
}
|
2018-03-12 17:19:12 +08:00
|
|
|
SDL_free((void *) server->serial);
|
2018-02-09 19:59:36 +08:00
|
|
|
}
|