Skip to content

Commit d518fa9

Browse files
authored
feat: add netstat utils (#18)
1 parent 323a9e6 commit d518fa9

File tree

8 files changed

+98
-2
lines changed

8 files changed

+98
-2
lines changed

.github/workflows/ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
strategy:
4545
matrix:
4646
node-version: [18, 20]
47+
name: node ${{ matrix.node-version }}
4748
steps:
4849
- uses: actions/checkout@v4
4950
- uses: pnpm/action-setup@v2
@@ -55,4 +56,5 @@ jobs:
5556
cache: 'pnpm'
5657

5758
- run: pnpm install --frozen-lockfile
59+
- run: pnpm build
5860
- run: pnpm test

package.json

+10
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@
5858
"types": "./dist/ip/index.d.cts",
5959
"default": "./dist/ip/index.cjs"
6060
}
61+
},
62+
"./netstat": {
63+
"import": {
64+
"types": "./dist/netstat/index.d.ts",
65+
"default": "./dist/netstat/index.js"
66+
},
67+
"require": {
68+
"types": "./dist/netstat/index.d.cts",
69+
"default": "./dist/netstat/index.cjs"
70+
}
6171
}
6272
},
6373
"author": "Shahrad Elahi <shahrad@litehex.com> (https://github.com/shahradelahi)",

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * as ip from './ip';
2+
export * as netstat from './netstat';

src/netstat/index.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { execShell } from '@/utils/exec-shell';
2+
import { removeDuplicate } from '@/utils/array';
3+
4+
// ------------------------
5+
6+
export interface ActiveConnection {
7+
protocol: string;
8+
program?: string;
9+
recvQ: number;
10+
sendQ: number;
11+
address: string;
12+
port: number;
13+
state?: string;
14+
}
15+
16+
export async function activeConnections(): Promise<ActiveConnection[]> {
17+
const { error, data } = await execShell(['netstat', '-tuapn']);
18+
if (error) {
19+
throw error;
20+
}
21+
22+
const lines = data.output.split('\n');
23+
const ports: ActiveConnection[] = [];
24+
for (const line of lines) {
25+
const match =
26+
/^(\w+)\s+(\d+)\s+(\d+)\s+((?:::)?(?:(?:(?:\d{1,3}\.){3}(?:\d{1,3}){1})?[0-9a-f]{0,4}:{0,2}){1,8}(?:::)?)\s+((?:::)?(?:(?:(?:\d{1,3}\.){3}(?:\d{1,3}){1})?[0-9a-f]{0,4}:{0,2}){1,8}(?:::)?\*?)\s+(\w+)?\s+(.*)$/.exec(
27+
line
28+
);
29+
if (match) {
30+
const port = match[4].split(':').at(-1);
31+
const address = match[4].replace(`:${port}`, '');
32+
const connection: ActiveConnection = {
33+
protocol: match[1],
34+
recvQ: Number(match[2]),
35+
sendQ: Number(match[3]),
36+
address: address,
37+
port: Number(port),
38+
state: match[6] ? match[6].trim() : undefined,
39+
program: match[7].trim(),
40+
};
41+
ports.push(connection);
42+
}
43+
}
44+
45+
return ports;
46+
}
47+
48+
export async function allocatedPorts(): Promise<number[]> {
49+
const cns = await activeConnections();
50+
return removeDuplicate(cns.map((cn) => cn.port));
51+
}

src/utils/array.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function removeDuplicate<T>(d: T[]): T[] {
2+
const final: T[] = [];
3+
for (const item of d) {
4+
if (!final.includes(item)) {
5+
final.push(item);
6+
}
7+
}
8+
return final;
9+
}

tests/netstat/index.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect } from 'chai';
2+
import { IPV4_REGEX } from '@/ip';
3+
import { activeConnections, allocatedPorts } from 'node-netkit/netstat';
4+
5+
describe('Active Connections', () => {
6+
it('should get active connections', async () => {
7+
const connections = await activeConnections();
8+
expect(connections).to.be.an('array');
9+
expect(connections.length).to.have.greaterThan(0);
10+
11+
expect(Object.keys(connections[0])).to.have.lengthOf(7);
12+
expect(connections[0].address).to.match(IPV4_REGEX);
13+
});
14+
15+
it('should get allocated ports', async () => {
16+
for (const p of await allocatedPorts()) {
17+
expect(p).to.be.greaterThan(0);
18+
expect(p).to.be.lessThan(65535);
19+
}
20+
});
21+
});

tsconfig.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
"allowJs": true,
2020
"baseUrl": ".",
2121
"paths": {
22-
"@/*": ["src/*"]
22+
"@/*": ["src/*"],
23+
"node-netkit/*": ["dist/*"],
24+
"node-netkit": ["dist/index.js"]
2325
},
2426
"outDir": "build"
2527
},

tsup.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineConfig } from 'tsup';
33
export default defineConfig({
44
clean: true,
55
dts: true,
6-
entry: ['src/index.ts', 'src/ip/index.ts'],
6+
entry: ['src/index.ts', 'src/ip/index.ts', 'src/netstat/index.ts'],
77
format: ['cjs', 'esm'],
88
target: 'esnext',
99
outDir: 'dist',

0 commit comments

Comments
 (0)