From 01fff962d1f6578b7cf5e6cdfb90f72df0743ac7 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sun, 8 Sep 2024 08:46:56 +0200 Subject: [PATCH] feat: Add a method to list device ports (#764) --- lib/tools/adb-commands.js | 54 +++++++++++++++++++++++ test/functional/adb-commands-e2e-specs.js | 9 ++++ 2 files changed, 63 insertions(+) diff --git a/lib/tools/adb-commands.js b/lib/tools/adb-commands.js index b5aa482a..06fc580d 100644 --- a/lib/tools/adb-commands.js +++ b/lib/tools/adb-commands.js @@ -2040,8 +2040,62 @@ methods.setDataState = async function setDataState (on, isEmulator = false) { await this.shell(['cmd', 'phone', 'data', on ? 'enable' : 'disable']); }; +/** + * Returns the list of TCP port states of the given family. + * Could be empty if no ports are opened. + * + * @this {import('../adb.js').ADB} + * @param {PortFamily} [family='4'] + * @returns {Promise} + */ +methods.listPorts = async function listPorts(family = '4') { + const sourceProcName = `/proc/net/tcp${family === '6' ? '6' : ''}`; + const output = await this.shell(['cat', sourceProcName]); + const lines = output.split('\n'); + if (_.isEmpty(lines)) { + log.debug(output); + throw new Error(`Cannot parse the payload of ${sourceProcName}`); + } + // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + const colHeaders = lines[0].split(/\s+/).filter(Boolean); + const localAddressCol = colHeaders.findIndex((x) => x === 'local_address'); + const stateCol = colHeaders.findIndex((x) => x === 'st'); + if (localAddressCol < 0 || stateCol < 0) { + log.debug(lines[0]); + throw new Error(`Cannot parse the header row of ${sourceProcName} payload`); + } + /** @type {PortInfo[]} */ + const result = []; + // 2: 1002000A:D036 24CE3AD8:01BB 08 00000000:00000000 00:00000000 00000000 10132 0 49104 1 0000000000000000 21 4 20 10 -1 + for (const line of lines.slice(1)) { + const values = line.split(/\s+/).filter(Boolean); + const portStr = values[localAddressCol]?.split(':')?.[1]; + const stateStr = values[stateCol]; + if (!portStr || !stateStr) { + continue; + } + result.push({ + port: parseInt(portStr, 16), + family, + state: parseInt(stateStr, 16), + }); + }; + return result; +}; + export default methods; /** * @typedef {typeof methods} ADBCommands */ + +/** + * @typedef {'4'|'6'} PortFamily + */ + +/** + * @typedef {Object} PortInfo + * @property {number} port The actual port number between 0 and 0xFFFF + * @property {PortFamily} family Either '4' or '6' + * @property {number} state See https://elixir.bootlin.com/linux/v4.14.42/source/include/net/tcp_states.h + */ diff --git a/test/functional/adb-commands-e2e-specs.js b/test/functional/adb-commands-e2e-specs.js index 1eacd5ce..f77e2a52 100644 --- a/test/functional/adb-commands-e2e-specs.js +++ b/test/functional/adb-commands-e2e-specs.js @@ -283,4 +283,13 @@ describe('adb commands', function () { _.isEmpty(await adb.takeScreenshot()).should.be.false; }); }); + + describe('listPorts', function () { + it('should list IPv4 ports', async function () { + _.isEmpty(await adb.listPorts()).should.be.false; + }); + it('should list IPv6 ports', async function () { + _.isEmpty(await adb.listPorts('6')).should.be.false; + }); + }); });