diff --git a/lib/ping.ts b/lib/ping.ts index e09cb77..be78bb3 100644 --- a/lib/ping.ts +++ b/lib/ping.ts @@ -40,68 +40,78 @@ function isValidHost(host: string): boolean { return /^[a-zA-Z0-9\-.:[\]-]+$/.test(domainToASCII(host)); } -export function ping( - host: string, - callback: (err: Error, res?: PingResult, stdout?: string) => void -): void { - let cmd: string, parseRegExp1: RegExp, parseRegExp2: RegExp; - // Validate input to prevent possible remote code execution - // Credit to Alex Hordijk for reporting this vulnerability - if (!isValidHost(host)) return callback(new Error("Invalid host")); - host = host.replace("[", "").replace("]", ""); - switch (platform()) { +export function parsePing(osPlatform: string, stdout: string): PingResult { + let parseRegExp1: RegExp, parseRegExp2: RegExp, parsed: PingResult; + switch (osPlatform) { case "linux": - cmd = `ping -w 1 -i 0.2 -c 3 ${host}`; parseRegExp1 = - /(\d+) packets transmitted, (\d+) received, ([\d.]+)% packet loss[^]*([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+)/; + /(\d+) packets transmitted, (\d+) .*received, ([\d.]+)% .*loss[^]*= ([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+)/; parseRegExp2 = - /(\d+) packets transmitted, (\d+) received, ([\d.]+)% packet loss/; + /(\d+) packets transmitted, (\d+) .*received, ([\d.]+)% .*loss/; break; case "freebsd": - // Send a single packet because on FreeBSD only superuser can send - // packets that are only 200 ms apart. - cmd = `ping -t 1 -c 3 ${host}`; parseRegExp1 = /(\d+) packets transmitted, (\d+) packets received, ([\d.]+)% packet loss\nround-trip min\/avg\/max\/stddev = ([\d.]+)\/([\d.]+)\/([\d.]+)\/([\d.]+) ms/; parseRegExp2 = /(\d+) packets transmitted, (\d+) packets received, ([\d.]+)% packet loss/; break; + } + const m1 = stdout.match(parseRegExp1); + if (m1) { + parsed = { + packetsTransmitted: +m1[1], + packetsReceived: +m1[2], + packetLoss: +m1[3], + min: +m1[4], + avg: +m1[5], + max: +m1[6], + mdev: +m1[7], + }; + } else { + const m2 = stdout.match(parseRegExp2); + if (m2) { + parsed = { + packetsTransmitted: +m2[1], + packetsReceived: +m2[2], + packetLoss: +m2[3], + min: null, + avg: null, + max: null, + mdev: null, + }; + } + } + return parsed; +} + +export function ping( + host: string, + callback: (err: Error, res?: PingResult, stdout?: string) => void +): void { + // Validate input to prevent possible remote code execution + // Credit to Alex Hordijk for reporting this vulnerability + if (!isValidHost(host)) return callback(new Error("Invalid host")); + host = host.replace("[", "").replace("]", ""); + let cmd: string; + + switch (platform()) { + case "linux": + cmd = `ping -w 1 -i 0.2 -c 3 ${host}`; + break; + case "freebsd": + // Send a single packet because on FreeBSD only superuser can send + // packets that are only 200 ms apart. + cmd = `ping -t 1 -c 3 ${host}`; + break; default: return callback(new Error("Platform not supported")); } exec(cmd, (err, stdout) => { - let parsed: PingResult; - if (stdout) { - const m1 = stdout.match(parseRegExp1); - if (m1) { - parsed = { - packetsTransmitted: +m1[1], - packetsReceived: +m1[2], - packetLoss: +m1[3], - min: +m1[4], - avg: +m1[5], - max: +m1[6], - mdev: +m1[7], - }; - } else { - const m2 = stdout.match(parseRegExp2); - if (m2) { - parsed = { - packetsTransmitted: +m2[1], - packetsReceived: +m2[2], - packetLoss: +m2[3], - min: null, - avg: null, - max: null, - mdev: null, - }; - } - } - } - - callback(err, parsed, stdout); + if (err) return callback(err); + const parsed: PingResult = parsePing(platform(), stdout); + return callback(err, parsed, stdout); }); } diff --git a/test/ping.ts b/test/ping.ts new file mode 100644 index 0000000..8014f87 --- /dev/null +++ b/test/ping.ts @@ -0,0 +1,36 @@ +import ava from "ava"; +import { parsePing } from "../lib/ping"; + +ava("linux Case-1", (t) => { + const platform = "linux"; + const stdout = + "PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data\n64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=6.66 ms\n64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=6.50 ms\n64 bytes from 8.8.8.8: icmp_seq=3 ttl=118 time=6.38 ms\n\n--- 8.8.8.8 ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 402ms\nrtt min/avg/max/mdev = 6.381/6.511/6.656/0.112 ms"; + const parsedResult = { + packetsTransmitted: 3, + packetsReceived: 3, + packetLoss: 0, + min: 6.381, + avg: 6.511, + max: 6.656, + mdev: 0.112, + }; + const parsed = parsePing(platform, stdout); + t.deepEqual(parsedResult, parsed); +}); + +ava("linux Case-2", (t) => { + const platform = "linux"; + const stdout = + "PING 10.251.9.108 (10.251.9.108): 56 data bytes\n64 bytes from 10.251.9.108: icmp_seq=0 ttl=57 time=36.758 ms\n64 bytes from 10.251.9.108: icmp_seq=1 ttl=57 time=69.094 ms\n64 bytes from 10.251.9.108: icmp_seq=2 ttl=57 time=28.868 ms\n--- 10.251.9.108 ping statistics ---3 packets transmitted, 3 packets received, 0% packet loss\nround-trip min/avg/max/stddev = 28.868/44.907/69.094/17.404 ms"; + const parsedResult = { + packetsTransmitted: 3, + packetsReceived: 3, + packetLoss: 0, + min: 28.868, + avg: 44.907, + max: 69.094, + mdev: 17.404, + }; + const parsed = parsePing(platform, stdout); + t.deepEqual(parsedResult, parsed); +});