Add function to find the device IP address
Parse the result of "adb shell ip route" to find the device IP address. PR #2827 <https://github.com/Genymobile/scrcpy/pull/2827>
This commit is contained in:
parent
b7e631791c
commit
f609b406c9
6 changed files with 217 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
||||||
src = [
|
src = [
|
||||||
'src/main.c',
|
'src/main.c',
|
||||||
'src/adb.c',
|
'src/adb.c',
|
||||||
|
'src/adb_parser.c',
|
||||||
'src/adb_tunnel.c',
|
'src/adb_tunnel.c',
|
||||||
'src/cli.c',
|
'src/cli.c',
|
||||||
'src/clock.c',
|
'src/clock.c',
|
||||||
|
@ -204,8 +205,14 @@ install_data('../data/icon.png',
|
||||||
# do not build tests in release (assertions would not be executed at all)
|
# do not build tests in release (assertions would not be executed at all)
|
||||||
if get_option('buildtype') == 'debug'
|
if get_option('buildtype') == 'debug'
|
||||||
tests = [
|
tests = [
|
||||||
|
['test_adb_parser', [
|
||||||
|
'tests/test_adb_parser.c',
|
||||||
|
'src/adb_parser.c',
|
||||||
|
'src/util/str.c',
|
||||||
|
'src/util/strbuf.c',
|
||||||
|
]],
|
||||||
['test_buffer_util', [
|
['test_buffer_util', [
|
||||||
'tests/test_buffer_util.c'
|
'tests/test_buffer_util.c',
|
||||||
]],
|
]],
|
||||||
['test_cbuf', [
|
['test_cbuf', [
|
||||||
'tests/test_cbuf.c',
|
'tests/test_cbuf.c',
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "adb_parser.h"
|
||||||
#include "util/file.h"
|
#include "util/file.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/process_intr.h"
|
#include "util/process_intr.h"
|
||||||
|
@ -389,3 +390,40 @@ adb_get_serialno(struct sc_intr *intr, unsigned flags) {
|
||||||
|
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags) {
|
||||||
|
const char *const cmd[] = {"shell", "ip", "route"};
|
||||||
|
|
||||||
|
sc_pipe pout;
|
||||||
|
sc_pid pid = adb_execute_p(serial, cmd, ARRAY_LEN(cmd), flags, &pout);
|
||||||
|
if (pid == SC_PROCESS_NONE) {
|
||||||
|
LOGD("Could not execute \"ip route\"");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "adb shell ip route" output should contain only a few lines
|
||||||
|
char buf[1024];
|
||||||
|
ssize_t r = sc_pipe_read_all_intr(intr, pid, pout, buf, sizeof(buf));
|
||||||
|
sc_pipe_close(pout);
|
||||||
|
|
||||||
|
bool ok = process_check_success_intr(intr, pid, "ip route", flags);
|
||||||
|
if (!ok) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((size_t) r <= sizeof(buf));
|
||||||
|
if (r == sizeof(buf) && buf[sizeof(buf) - 1] != '\0') {
|
||||||
|
// The implementation assumes that the output of "ip route" fits in the
|
||||||
|
// buffer in a single pass
|
||||||
|
LOGW("Result of \"ip route\" does not fit in 1Kb. "
|
||||||
|
"Please report an issue.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sc_adb_parse_device_ip_from_output(buf, r);
|
||||||
|
}
|
||||||
|
|
|
@ -66,4 +66,13 @@ adb_disconnect(struct sc_intr *intr, const char *ip_port, unsigned flags);
|
||||||
char *
|
char *
|
||||||
adb_get_serialno(struct sc_intr *intr, unsigned flags);
|
adb_get_serialno(struct sc_intr *intr, unsigned flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to retrieve the device IP
|
||||||
|
*
|
||||||
|
* Return the IP as a string of the form "xxx.xxx.xxx.xxx", to be freed by the
|
||||||
|
* caller, or NULL on error.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
adb_get_device_ip(struct sc_intr *intr, const char *serial, unsigned flags);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
65
app/src/adb_parser.c
Normal file
65
app/src/adb_parser.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include "adb_parser.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/str.h"
|
||||||
|
|
||||||
|
static char *
|
||||||
|
sc_adb_parse_device_ip_from_line(char *line, size_t len) {
|
||||||
|
// One line from "ip route" looks lile:
|
||||||
|
// "192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.x"
|
||||||
|
|
||||||
|
// Get the location of the device name (index of "wlan0" in the example)
|
||||||
|
ssize_t idx_dev_name = sc_str_index_of_column(line, 2, " ");
|
||||||
|
if (idx_dev_name == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the location of the ip address (column 8, but column 6 if we start
|
||||||
|
// from column 2). Must be computed before truncating individual columns.
|
||||||
|
ssize_t idx_ip = sc_str_index_of_column(&line[idx_dev_name], 6, " ");
|
||||||
|
if (idx_ip == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// idx_ip is searched from &line[idx_dev_name]
|
||||||
|
idx_ip += idx_dev_name;
|
||||||
|
|
||||||
|
char *dev_name = &line[idx_dev_name];
|
||||||
|
sc_str_truncate(dev_name, len - idx_dev_name + 1, " \t");
|
||||||
|
|
||||||
|
char *ip = &line[idx_ip];
|
||||||
|
sc_str_truncate(ip, len - idx_ip + 1, " \t");
|
||||||
|
|
||||||
|
// Only consider lines where the device name starts with "wlan"
|
||||||
|
if (strncmp(dev_name, "wlan", sizeof("wlan") - 1)) {
|
||||||
|
LOGD("Device ip lookup: ignoring %s (%s)", ip, dev_name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
sc_adb_parse_device_ip_from_output(char *buf, size_t buf_len) {
|
||||||
|
size_t idx_line = 0;
|
||||||
|
while (idx_line < buf_len && buf[idx_line] != '\0') {
|
||||||
|
char *line = &buf[idx_line];
|
||||||
|
size_t len = sc_str_truncate(line, buf_len - idx_line, "\n");
|
||||||
|
|
||||||
|
// The same, but without any trailing '\r'
|
||||||
|
size_t line_len = sc_str_remove_trailing_cr(line, len);
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_line(line, line_len);
|
||||||
|
if (ip) {
|
||||||
|
// Found
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next line starts after the '\n' (replaced by `\0`)
|
||||||
|
idx_line += len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
14
app/src/adb_parser.h
Normal file
14
app/src/adb_parser.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef SC_ADB_PARSER_H
|
||||||
|
#define SC_ADB_PARSER_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include "stddef.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the ip from the output of `adb shell ip route`
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
sc_adb_parse_device_ip_from_output(char *buf, size_t buf_len);
|
||||||
|
|
||||||
|
#endif
|
83
app/tests/test_adb_parser.c
Normal file
83
app/tests/test_adb_parser.c
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "adb_parser.h"
|
||||||
|
|
||||||
|
static void test_get_ip_single_line() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
|
"192.168.12.34\r\r\n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(ip);
|
||||||
|
assert(!strcmp(ip, "192.168.12.34"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_single_line_without_eol() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
|
"192.168.12.34";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(ip);
|
||||||
|
assert(!strcmp(ip, "192.168.12.34"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_single_line_with_trailing_space() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
|
"192.168.12.34 \n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(ip);
|
||||||
|
assert(!strcmp(ip, "192.168.12.34"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_multiline_first_ok() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
|
"192.168.1.2\r\n"
|
||||||
|
"10.0.0.0/24 dev rmnet proto kernel scope link src "
|
||||||
|
"10.0.0.2\r\n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(ip);
|
||||||
|
assert(!strcmp(ip, "192.168.1.2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_multiline_second_ok() {
|
||||||
|
char ip_route[] = "10.0.0.0/24 dev rmnet proto kernel scope link src "
|
||||||
|
"10.0.0.3\r\n"
|
||||||
|
"192.168.1.0/24 dev wlan0 proto kernel scope link src "
|
||||||
|
"192.168.1.3\r\n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(ip);
|
||||||
|
assert(!strcmp(ip, "192.168.1.3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_no_wlan() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev rmnet proto kernel scope link src "
|
||||||
|
"192.168.12.34\r\r\n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(!ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_get_ip_truncated() {
|
||||||
|
char ip_route[] = "192.168.1.0/24 dev rmnet proto kernel scope link src "
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
char *ip = sc_adb_parse_device_ip_from_output(ip_route, sizeof(ip_route));
|
||||||
|
assert(!ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
test_get_ip_single_line();
|
||||||
|
test_get_ip_single_line_without_eol();
|
||||||
|
test_get_ip_single_line_with_trailing_space();
|
||||||
|
test_get_ip_multiline_first_ok();
|
||||||
|
test_get_ip_multiline_second_ok();
|
||||||
|
test_get_ip_no_wlan();
|
||||||
|
test_get_ip_truncated();
|
||||||
|
}
|
Loading…
Reference in a new issue