tcpie 9.0.1 → 10.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -38
- package/bin/tcpie.js +9 -0
- package/index.js +11 -21
- package/package.json +19 -31
- package/LICENSE +0 -22
- package/tcpie.js +0 -245
package/README.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# tcpie
|
|
2
|
-
[](https://www.npmjs.org/package/tcpie) [](https://www.npmjs.org/package/tcpie)
|
|
2
|
+
[](https://www.npmjs.org/package/tcpie) [](https://www.npmjs.org/package/tcpie) [](https://packagephobia.com/result?p=tcpie)
|
|
3
|
+
|
|
3
4
|
> Ping any TCP port
|
|
4
5
|
|
|
5
6
|
tcpie is a tool to measure latency and verify the reliabilty of a TCP connection. It does so by initiating a handshake followed by an immediately termination of the socket. While many existing tools require raw socket access, tcpie runs fine in user space. An API for use as a module is also provided.
|
|
6
7
|
|
|
7
8
|
## CLI
|
|
9
|
+
|
|
8
10
|
### Installation
|
|
9
|
-
Install [Node.js](https://nodejs.org) and then do:
|
|
10
11
|
```
|
|
11
12
|
$ npm i -g tcpie
|
|
12
13
|
```
|
|
@@ -24,52 +25,27 @@ connected to google.com:443 seq=5 srcport=59057 time=10.4 ms
|
|
|
24
25
|
5 handshakes attempted, 5 succeeded, 0% failed
|
|
25
26
|
rtt min/avg/max/stdev = 10.012/10.970/12.854/1.190 ms
|
|
26
27
|
```
|
|
27
|
-
## Usage
|
|
28
|
-
```
|
|
29
|
-
Usage: tcpie [options] host[:port]|url [port|22]
|
|
30
|
-
|
|
31
|
-
Options:
|
|
32
|
-
|
|
33
|
-
-v, --version output version
|
|
34
|
-
-c, --count <n> number of connects (default: infinite)
|
|
35
|
-
-i, --interval <n> wait n seconds between connects (default: 1)
|
|
36
|
-
-t, --timeout <n> connection timeout in seconds (default: 3)
|
|
37
|
-
-T, --timestamp add timestamps to output
|
|
38
|
-
-f, --flood flood mode, connect as fast as possible
|
|
39
|
-
-C, --no-color disable color output
|
|
40
|
-
|
|
41
|
-
Examples:
|
|
42
28
|
|
|
43
|
-
|
|
44
|
-
$ tcpie -i .1 8.8.8.8:53
|
|
45
|
-
$ tcpie -c5 -t.05 aspmx.l.google.com 25
|
|
46
|
-
$ tcpie -i.2 https://google.com
|
|
29
|
+
## API
|
|
47
30
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
## Module API
|
|
51
|
-
### Installation
|
|
52
|
-
```
|
|
53
|
-
$ npm i tcpie
|
|
54
|
-
```
|
|
55
|
-
### Example
|
|
31
|
+
### Usage
|
|
56
32
|
```js
|
|
57
|
-
|
|
58
|
-
const pie = tcpie(
|
|
33
|
+
import {tcpie} from "tcpie";
|
|
34
|
+
const pie = tcpie("google.com", 443, {count: 10, interval: 500, timeout: 2000});
|
|
59
35
|
|
|
60
|
-
pie.on(
|
|
61
|
-
console.info(
|
|
62
|
-
}).on(
|
|
36
|
+
pie.on("connect", function(stats) {
|
|
37
|
+
console.info("connect", stats);
|
|
38
|
+
}).on("error", function(err, stats) {
|
|
63
39
|
console.error(err, stats);
|
|
64
|
-
}).on(
|
|
65
|
-
console.info(
|
|
66
|
-
}).on(
|
|
40
|
+
}).on("timeout", function(stats) {
|
|
41
|
+
console.info("timeout", stats);
|
|
42
|
+
}).on("end", function(stats) {
|
|
67
43
|
console.info(stats);
|
|
68
44
|
// -> {
|
|
69
45
|
// -> sent: 10,
|
|
70
46
|
// -> success: 10,
|
|
71
47
|
// -> failed: 0,
|
|
72
|
-
// -> target: { host:
|
|
48
|
+
// -> target: { host: "google.com", port: 443 }
|
|
73
49
|
// -> }
|
|
74
50
|
}).start();
|
|
75
51
|
```
|
package/bin/tcpie.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {createRequire} from 'module';const require = createRequire(import.meta.url);
|
|
3
|
+
var vt=Object.create;var k=Object.defineProperty;var xt=Object.getOwnPropertyDescriptor;var Ot=Object.getOwnPropertyNames;var wt=Object.getPrototypeOf,It=Object.prototype.hasOwnProperty;var tt=(t,o)=>()=>(o||t((o={exports:{}}).exports,o),o.exports);var Ct=(t,o,e,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let n of Ot(o))!It.call(t,n)&&n!==e&&k(t,n,{get:()=>o[n],enumerable:!(s=xt(o,n))||s.enumerable});return t};var ot=(t,o,e)=>(e=t!=null?vt(wt(t)):{},Ct(o||!t||!t.__esModule?k(e,"default",{value:t,enumerable:!0}):e,t));var it=tt((Xt,rt)=>{(function(){"use strict";function t(o){if(!Array.isArray(o))throw new TypeError("stdev()::invalid input argument. Must provide an array.");var e=o.length,s=0,n=0,c=0,i=0;if(e<2)return 0;for(var N=0;N<e;N++)s+=1,i=o[N]-n,n+=i/s,c+=i*(o[N]-n);return Math.sqrt(c/(s-1))}rt.exports=t})()});var pt=tt((eo,lt)=>{"use strict";function Ft(t,o){var e=t;o.slice(0,-1).forEach(function(n){e=e[n]||{}});var s=o[o.length-1];return s in e}function ft(t){return typeof t=="number"||/^0x[0-9a-f]+$/i.test(t)?!0:/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(t)}function ut(t,o){return o==="constructor"&&typeof t[o]=="function"||o==="__proto__"}lt.exports=function(t,o){o||(o={});var e={bools:{},strings:{},unknownFn:null};typeof o.unknown=="function"&&(e.unknownFn=o.unknown),typeof o.boolean=="boolean"&&o.boolean?e.allBools=!0:[].concat(o.boolean).filter(Boolean).forEach(function(r){e.bools[r]=!0});var s={};function n(r){return s[r].some(function(m){return e.bools[m]})}Object.keys(o.alias||{}).forEach(function(r){s[r]=[].concat(o.alias[r]),s[r].forEach(function(m){s[m]=[r].concat(s[r].filter(function(w){return m!==w}))})}),[].concat(o.string).filter(Boolean).forEach(function(r){e.strings[r]=!0,s[r]&&[].concat(s[r]).forEach(function(m){e.strings[m]=!0})});var c=o.default||{},i={_:[]};function N(r,m){return e.allBools&&/^--[^=]+$/.test(m)||e.strings[r]||e.bools[r]||s[r]}function P(r,m,w){for(var u=r,S=0;S<m.length-1;S++){var x=m[S];if(ut(u,x))return;u[x]===void 0&&(u[x]={}),(u[x]===Object.prototype||u[x]===Number.prototype||u[x]===String.prototype)&&(u[x]={}),u[x]===Array.prototype&&(u[x]=[]),u=u[x]}var I=m[m.length-1];ut(u,I)||((u===Object.prototype||u===Number.prototype||u===String.prototype)&&(u={}),u===Array.prototype&&(u=[]),u[I]===void 0||e.bools[I]||typeof u[I]=="boolean"?u[I]=w:Array.isArray(u[I])?u[I].push(w):u[I]=[u[I],w])}function _(r,m,w){if(!(w&&e.unknownFn&&!N(r,w)&&e.unknownFn(w)===!1)){var u=!e.strings[r]&&ft(m)?Number(m):m;P(i,r.split("."),u),(s[r]||[]).forEach(function(S){P(i,S.split("."),u)})}}Object.keys(e.bools).forEach(function(r){_(r,c[r]===void 0?!1:c[r])});var y=[];t.indexOf("--")!==-1&&(y=t.slice(t.indexOf("--")+1),t=t.slice(0,t.indexOf("--")));for(var h=0;h<t.length;h++){var f=t[h],p,T;if(/^--.+=/.test(f)){var j=f.match(/^--([^=]+)=([\s\S]*)$/);p=j[1];var V=j[2];e.bools[p]&&(V=V!=="false"),_(p,V,f)}else if(/^--no-.+/.test(f))p=f.match(/^--no-(.+)/)[1],_(p,!1,f);else if(/^--.+/.test(f))p=f.match(/^--(.+)/)[1],T=t[h+1],T!==void 0&&!/^(-|--)[^-]/.test(T)&&!e.bools[p]&&!e.allBools&&(!s[p]||!n(p))?(_(p,T,f),h+=1):/^(true|false)$/.test(T)?(_(p,T==="true",f),h+=1):_(p,e.strings[p]?"":!0,f);else if(/^-[^-]+/.test(f)){for(var $=f.slice(1,-1).split(""),L=!1,b=0;b<$.length;b++){if(T=f.slice(b+2),T==="-"){_($[b],T,f);continue}if(/[A-Za-z]/.test($[b])&&T[0]==="="){_($[b],T.slice(1),f),L=!0;break}if(/[A-Za-z]/.test($[b])&&/-?\d+(\.\d*)?(e-?\d+)?$/.test(T)){_($[b],T,f),L=!0;break}if($[b+1]&&$[b+1].match(/\W/)){_($[b],f.slice(b+2),f),L=!0;break}else _($[b],e.strings[$[b]]?"":!0,f)}p=f.slice(-1)[0],!L&&p!=="-"&&(t[h+1]&&!/^(-|--)[^-]/.test(t[h+1])&&!e.bools[p]&&(!s[p]||!n(p))?(_(p,t[h+1],f),h+=1):t[h+1]&&/^(true|false)$/.test(t[h+1])?(_(p,t[h+1]==="true",f),h+=1):_(p,e.strings[p]?"":!0,f))}else if((!e.unknownFn||e.unknownFn(f)!==!1)&&i._.push(e.strings._||!ft(f)?f:Number(f)),o.stopEarly){i._.push.apply(i._,t.slice(h+1));break}}return Object.keys(c).forEach(function(r){Ft(i,r.split("."))||(P(i,r.split("."),c[r]),(s[r]||[]).forEach(function(m){P(i,m.split("."),c[r])}))}),o["--"]?i["--"]=y.slice():y.forEach(function(r){i._.push(r)}),i}});var et=!0;function nt(){et=!1}function q(t,o,e){return et?`\x1B[${o}m${t.join(" ")}\x1B[${e}m`:t.join(" ")}var C=(...t)=>q(t,31,39),K=(...t)=>q(t,32,39),st=(...t)=>q(t,33,39);var Tt=ot(it(),1);import{isIP as Dt}from"node:net";import{lookup as Ut}from"node:dns";import M,{exit as R,argv as Yt,stdin as J,stdout as Q,stderr as yt}from"node:process";import gt from"node:events";import Nt from"node:net";import At from"node:util";var A=function(t,o,e){if(!(this instanceof A))return new A;if(typeof t!="string")throw new Error("host is required");o===void 0&&(o=80),this.host=t,this.port=o,this.opts={interval:1e3,timeout:3e3,count:1/0,...e},this.stats={sent:0,success:0,failed:0}};At.inherits(A,gt.EventEmitter);A.prototype.start=function t(o){return o||(this.stats.sent=0,this.stats.success=0,this.stats.failed=0),this._next=setTimeout(t.bind(this,!0),this.opts.interval),this._done=!1,this._abort=!1,this._socket=new Nt.Socket,this._startTime=performance.now(),this._socket.setTimeout(this.opts.timeout),this._socket.on("timeout",()=>{this._done||(this._done=!0,this.stats.sent++,this.stats.failed++,this.emit("timeout",H(this,this)),this._socket.destroy(),B(this))}),this._socket.on("error",e=>{this._done||(this._done=!0,this.stats.sent++,this.stats.failed++,this.emit("error",e,H(this,this)),this._socket.destroy(),B(this))}),this._socket.connect(this.port,this.host,()=>{this._done||(this._done=!0,this.stats.sent++,this.stats.success++,this.stats.rtt=performance.now()-this._startTime,this.emit("connect",H(this,this)),this._socket.end(),B(this))}),this};A.prototype.stop=function(){return this._abort=!0,this._socket.end(),B(this),this};function ct(...t){return new A(...t)}function H(t,o){let e=t.stats;return e.target={host:t.host,port:t.port},e.socket={localAddress:o.localAddress,localPort:o.localPort,remoteAddress:o.remoteAddress,remotePort:o.remotePort},e}function B(t){(t._abort||t.stats.failed+t.stats.success>=t.opts.count)&&(t._next&&clearTimeout(t._next),t.emit("end",{sent:t.stats.sent,success:t.stats.success,failed:t.stats.failed,target:{host:t.host,port:t.port}}))}var Et=ot(pt(),1);import{readFileSync as St}from"node:fs";var W;function at(t,o="tcp"){if(typeof t!="string")throw new Error("expected a 'string'");W||(W=JSON.parse(St(new URL("services.json",import.meta.url))));let e=W[t.toLowerCase()];if(!e)return null;let s=e.ports.find(n=>/\w+$/.exec(n)[0]===o);return s?{port:Number(/^\d+/.exec(s)[0]),protocol:/\w+$/.exec(s)[0],description:e.description}:null}import z from"node:process";import Mt from"node:os";import mt from"node:tty";function E(t,o=globalThis.Deno?globalThis.Deno.args:z.argv){let e=t.startsWith("-")?"":t.length===1?"-":"--",s=o.indexOf(e+t),n=o.indexOf("--");return s!==-1&&(n===-1||s<n)}var{env:a}=z,G;E("no-color")||E("no-colors")||E("color=false")||E("color=never")?G=0:(E("color")||E("colors")||E("color=true")||E("color=always"))&&(G=1);function Pt(){if("FORCE_COLOR"in a)return a.FORCE_COLOR==="true"?1:a.FORCE_COLOR==="false"?0:a.FORCE_COLOR.length===0?1:Math.min(Number.parseInt(a.FORCE_COLOR,10),3)}function Lt(t){return t===0?!1:{level:t,hasBasic:!0,has256:t>=2,has16m:t>=3}}function Bt(t,{streamIsTTY:o,sniffFlags:e=!0}={}){let s=Pt();s!==void 0&&(G=s);let n=e?G:s;if(n===0)return 0;if(e){if(E("color=16m")||E("color=full")||E("color=truecolor"))return 3;if(E("color=256"))return 2}if("TF_BUILD"in a&&"AGENT_NAME"in a)return 1;if(t&&!o&&n===void 0)return 0;let c=n||0;if(a.TERM==="dumb")return c;if(z.platform==="win32"){let i=Mt.release().split(".");return Number(i[0])>=10&&Number(i[2])>=10586?Number(i[2])>=14931?3:2:1}if("CI"in a)return"GITHUB_ACTIONS"in a?3:["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI","BUILDKITE","DRONE"].some(i=>i in a)||a.CI_NAME==="codeship"?1:c;if("TEAMCITY_VERSION"in a)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(a.TEAMCITY_VERSION)?1:0;if(a.COLORTERM==="truecolor"||a.TERM==="xterm-kitty")return 3;if("TERM_PROGRAM"in a){let i=Number.parseInt((a.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(a.TERM_PROGRAM){case"iTerm.app":return i>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(a.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(a.TERM)||"COLORTERM"in a?1:c}function dt(t,o={}){let e=Bt(t,{streamIsTTY:t&&t.isTTY,...o});return Lt(e)}var Gt={stdout:dt({isTTY:mt.isatty(1)}),stderr:dt({isTTY:mt.isatty(2)})},ht=Gt;var l=(0,Et.default)(Yt.slice(2),{boolean:["color","C","timestamp","T","flood","f","version","v"]}),Vt="10.0.1",qt=1,D=3,Kt=0,Ht=22,Wt=[""," Usage: tcpie [options] host[:port]|url [port|22]",""," Options:",""," -v, --version output version"," -c, --count <n> number of connects (default: infinite)"," -i, --interval <n> wait n seconds between connects (default: 1)"," -t, --timeout <n> connection timeout in seconds (default: 3)"," -T, --timestamp add timestamps to output"," -f, --flood flood mode, connect as fast as possible"," -C, --no-color disable color output",""," Examples:",""," $ tcpie google.com"," $ tcpie -i .1 8.8.8.8:53"," $ tcpie -c5 -t.05 aspmx.l.google.com 25"," $ tcpie -i.2 https://google.com","",""].join(`
|
|
4
|
+
`);l.v&&(console.info(Vt),R(0));(!l._.length||l._.length>2||l._[1]&&Number.isNaN(parseInt(l._[1])))&&Rt();var d=l._[0],F={},O=parseInt(l._[1]),X=!1,Y=[],v;typeof d!="string"&&Rt();var U=/^(.+):(\d+)$/.exec(d);U&&U.length===3&&!O&&(d=U[1],O=U[2]);if(/.+:\/\/.+/.test(d)){let{protocol:t,hostname:o,port:e}=new URL(d),s=t.replace(":","");d=o,O=e??at(s).port,O||(g(C("ERROR:"),`Unknown protocol '${s}'`),R(1)),d||(g(C("ERROR:"),`Missing host in '${d}'`),R(1))}O||(O=Ht);(l.count||l.c)&&(F.count=parseInt(l.count||l.c));(l.interval||l.i)&&(F.interval=$t(l.interval||l.i));(l.timeout||l.t)&&(F.timeout=$t(l.timeout||l.t));(l.flood||l.f)&&(F.interval=0);(l.C||!ht.stdout)&&nt();Dt(d)?(bt(d,d,O),_t(d,O,F)):Ut(d,(t,o)=>{t?(t.code==="ENOTFOUND"?g(C("ERROR:"),`Host '${d}' not found`):g(C("ERROR:"),t.code,t.syscall||""),R(1)):(bt(d,o,O),_t(d,O,F))});function _t(t,o,e){let s=ct(t,o,e);s.on("error",(n,c)=>{v=c,g(C("error connecting to",`${c.target.host}:${c.target.port}`),`seq=${c.sent}`,`error=${C(n.code)}`)}).on("connect",n=>{v=n,Y.push(n.rtt),g(K("connected to",`${n.target.host}:${n.target.port}`),`seq=${n.sent}`,n.socket.localPort!==void 0?`srcport=${n.socket.localPort}`:"",`time=${zt(n.rtt.toFixed(qt))}`)}).on("timeout",n=>{v=n,g(C("timeout connecting to",`${n.target.host}:${n.target.port}`),`seq=${n.sent}`,n.socket.localPort&&`srcport=${n.socket.localPort}`)}),J.isTTY?(J.setRawMode(!0),J.on("data",n=>{let c=[3,4,26,28];for(let i=0;i<n.length;i++)c.includes(n[i])&&Z()})):(M.on("SIGINT",R),M.on("SIGQUIT",R),M.on("SIGTERM",R),M.on("SIGTSTP",R)),M.on("exit",Z),s.on("end",Z).start()}function bt(t,o,e){g("TCPIE",t,`(${o})`,"port",String(e))}function Z(){let t=0,o=1/0,e=0,s,n;if(X&&R(v.success===0&&1||0),v&&v.sent>0){for(let c of Y)c<=o&&(o=c.toFixed(D)),c>=e&&(e=c.toFixed(D)),t+=c;s=(t/Y.length).toFixed(D),n=(0,Tt.default)(Y).toFixed(D),o===1/0&&(o="0"),Number.isNaN(s)&&(s="0"),X=!0,g(`
|
|
5
|
+
---`,d,"tcpie statistics","---",`
|
|
6
|
+
${v.sent}`,"handshakes attempted,",v.success||"0","succeeded,",`${(v.failed/v.sent*100).toFixed(Kt)}% failed`,`
|
|
7
|
+
rtt min/avg/max/stdev =`,`${o}/${s}/${e}/${n}`,"ms"),R(v.success===0&&1||0)}else R(1)}function zt(t){return t>=150?`${C(t)} ms`:t>=75?`${st(t)} ms`:`${K(t)} ms`}function g(...t){t=t.filter(Boolean),(l.timeout||l.T)&&t[0][0]!==`
|
|
8
|
+
`&&t.unshift(Jt()),t.push(`
|
|
9
|
+
`),(Q._type==="pipe"&&X?yt:Q).write(t.join(" "))}function Rt(){Q.write(Wt),R(1)}function $t(t){return parseFloat(t)*1e3}function Jt(){let t=new Date,o=t.getFullYear(),e=t.getMonth()+1,s=t.getDate(),n=t.getHours(),c=t.getMinutes(),i=t.getSeconds();return e<10&&(e=`0${e}`),s<10&&(s=`0${s}`),n<10&&(n=`0${n}`),c<10&&(c=`0${c}`),i<10&&(i=`0${i}`),`${o}-${e}-${s} ${n}:${c}:${i}`}
|
package/index.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const net = require("net");
|
|
5
|
-
const util = require("util");
|
|
1
|
+
import events from "node:events";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
import util from "node:util";
|
|
6
4
|
|
|
7
5
|
const Tcpie = function(host, port, opts) {
|
|
8
6
|
if (!(this instanceof Tcpie)) return new Tcpie();
|
|
9
7
|
if (typeof host !== "string") throw new Error("host is required");
|
|
10
|
-
if (
|
|
8
|
+
if (port === undefined) port = 80;
|
|
11
9
|
|
|
12
10
|
this.host = host;
|
|
13
11
|
this.port = port;
|
|
14
12
|
|
|
15
|
-
this.opts =
|
|
16
|
-
interval: 1000,
|
|
13
|
+
this.opts = {interval: 1000,
|
|
17
14
|
timeout: 3000,
|
|
18
|
-
count: Infinity
|
|
19
|
-
}, opts);
|
|
15
|
+
count: Infinity, ...opts};
|
|
20
16
|
|
|
21
17
|
this.stats = {
|
|
22
18
|
sent: 0,
|
|
@@ -38,7 +34,7 @@ Tcpie.prototype.start = function start(subsequent) {
|
|
|
38
34
|
this._done = false;
|
|
39
35
|
this._abort = false;
|
|
40
36
|
this._socket = new net.Socket();
|
|
41
|
-
this._startTime = now();
|
|
37
|
+
this._startTime = performance.now();
|
|
42
38
|
|
|
43
39
|
this._socket.setTimeout(this.opts.timeout);
|
|
44
40
|
this._socket.on("timeout", () => {
|
|
@@ -68,7 +64,7 @@ Tcpie.prototype.start = function start(subsequent) {
|
|
|
68
64
|
this._done = true;
|
|
69
65
|
this.stats.sent++;
|
|
70
66
|
this.stats.success++;
|
|
71
|
-
this.stats.rtt = (now() - this._startTime)
|
|
67
|
+
this.stats.rtt = (performance.now() - this._startTime);
|
|
72
68
|
this.emit("connect", addDetails(this, this));
|
|
73
69
|
this._socket.end();
|
|
74
70
|
checkEnd(this);
|
|
@@ -85,9 +81,9 @@ Tcpie.prototype.stop = function stop() {
|
|
|
85
81
|
return this;
|
|
86
82
|
};
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
return new Tcpie(
|
|
90
|
-
}
|
|
84
|
+
export function tcpie(...args) {
|
|
85
|
+
return new Tcpie(...args);
|
|
86
|
+
}
|
|
91
87
|
|
|
92
88
|
// add details to stats object
|
|
93
89
|
function addDetails(that, socket) {
|
|
@@ -124,9 +120,3 @@ function checkEnd(that) {
|
|
|
124
120
|
});
|
|
125
121
|
}
|
|
126
122
|
}
|
|
127
|
-
|
|
128
|
-
// get current timestamp in nanoseconds
|
|
129
|
-
function now() {
|
|
130
|
-
const hrtime = process.hrtime();
|
|
131
|
-
return hrtime[0] * 1e9 + hrtime[1];
|
|
132
|
-
}
|
package/package.json
CHANGED
|
@@ -1,46 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tcpie",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.1",
|
|
4
4
|
"description": "Ping any TCP port",
|
|
5
|
-
"author": "silverwind <me@silverwind.io>
|
|
5
|
+
"author": "silverwind <me@silverwind.io>",
|
|
6
6
|
"repository": "silverwind/tcpie",
|
|
7
7
|
"license": "BSD-2-Clause",
|
|
8
|
-
"
|
|
9
|
-
"bin": "tcpie.js",
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
},
|
|
8
|
+
"exports": "./index.js",
|
|
9
|
+
"bin": "./bin/tcpie.js",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
13
12
|
"engines": {
|
|
14
|
-
"node": ">=
|
|
13
|
+
"node": ">=16"
|
|
15
14
|
},
|
|
16
15
|
"files": [
|
|
17
|
-
"index.js",
|
|
18
|
-
"tcpie.js"
|
|
19
|
-
],
|
|
20
|
-
"keywords": [
|
|
21
|
-
"tcp",
|
|
22
|
-
"port",
|
|
23
|
-
"socket",
|
|
24
|
-
"ping",
|
|
25
|
-
"network",
|
|
26
|
-
"networking",
|
|
27
|
-
"connect",
|
|
28
|
-
"connection",
|
|
29
|
-
"test",
|
|
30
|
-
"testing"
|
|
16
|
+
"./index.js",
|
|
17
|
+
"./bin/tcpie.js"
|
|
31
18
|
],
|
|
32
19
|
"dependencies": {
|
|
33
|
-
"chalk": "4.0.0",
|
|
34
20
|
"compute-stdev": "1.0.0",
|
|
35
|
-
"
|
|
36
|
-
"minimist": "1.2.
|
|
37
|
-
"port-numbers": "
|
|
21
|
+
"glowie": "1.1.0",
|
|
22
|
+
"minimist": "1.2.8",
|
|
23
|
+
"port-numbers": "7.0.0",
|
|
24
|
+
"supports-color": "9.3.1"
|
|
38
25
|
},
|
|
39
26
|
"devDependencies": {
|
|
40
|
-
"eslint": "
|
|
41
|
-
"eslint-config-silverwind": "
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
27
|
+
"eslint": "8.43.0",
|
|
28
|
+
"eslint-config-silverwind": "73.0.4",
|
|
29
|
+
"updates": "14.2.8",
|
|
30
|
+
"versions": "11.0.1",
|
|
31
|
+
"vitest": "0.32.2",
|
|
32
|
+
"vitest-config-silverwind": "1.0.0"
|
|
45
33
|
}
|
|
46
34
|
}
|
package/LICENSE
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Copyright (c) silverwind
|
|
2
|
-
All rights reserved.
|
|
3
|
-
|
|
4
|
-
Redistribution and use in source and binary forms, with or without
|
|
5
|
-
modification, are permitted provided that the following conditions are met:
|
|
6
|
-
|
|
7
|
-
1. Redistributions of source code must retain the above copyright notice, this
|
|
8
|
-
list of conditions and the following disclaimer.
|
|
9
|
-
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
-
this list of conditions and the following disclaimer in the documentation
|
|
11
|
-
and/or other materials provided with the distribution.
|
|
12
|
-
|
|
13
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
14
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
15
|
-
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
17
|
-
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
18
|
-
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
19
|
-
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
20
|
-
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
21
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
22
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/tcpie.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
const pkg = require("./package.json");
|
|
5
|
-
|
|
6
|
-
// avoid EPIPE on partially consumed streams
|
|
7
|
-
require("epipebomb")();
|
|
8
|
-
|
|
9
|
-
const chalk = require("chalk");
|
|
10
|
-
const net = require("net");
|
|
11
|
-
const dns = require("dns");
|
|
12
|
-
const stdev = require("compute-stdev");
|
|
13
|
-
const tcpie = require(".");
|
|
14
|
-
|
|
15
|
-
const args = require("minimist")(process.argv.slice(2), {
|
|
16
|
-
boolean: [
|
|
17
|
-
"color", "C",
|
|
18
|
-
"timestamp", "T",
|
|
19
|
-
"flood", "f",
|
|
20
|
-
"version", "v"
|
|
21
|
-
]
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const DIGITS_LINE = 1;
|
|
25
|
-
const DIGITS_STATS = 3;
|
|
26
|
-
const DIGITS_PERC = 0;
|
|
27
|
-
const DEFAULT_PORT = 22;
|
|
28
|
-
|
|
29
|
-
const usage = [
|
|
30
|
-
"",
|
|
31
|
-
" Usage: tcpie [options] host[:port]|url [port|22]",
|
|
32
|
-
"",
|
|
33
|
-
" Options:",
|
|
34
|
-
"",
|
|
35
|
-
" -v, --version output version",
|
|
36
|
-
" -c, --count <n> number of connects (default: infinite)",
|
|
37
|
-
" -i, --interval <n> wait n seconds between connects (default: 1)",
|
|
38
|
-
" -t, --timeout <n> connection timeout in seconds (default: 3)",
|
|
39
|
-
" -T, --timestamp add timestamps to output",
|
|
40
|
-
" -f, --flood flood mode, connect as fast as possible",
|
|
41
|
-
" -C, --no-color disable color output",
|
|
42
|
-
"",
|
|
43
|
-
" Examples:",
|
|
44
|
-
"",
|
|
45
|
-
" $ tcpie google.com",
|
|
46
|
-
" $ tcpie -i .1 8.8.8.8:53",
|
|
47
|
-
" $ tcpie -c5 -t.05 aspmx.l.google.com 25",
|
|
48
|
-
" $ tcpie -i.2 https://google.com",
|
|
49
|
-
"",
|
|
50
|
-
""
|
|
51
|
-
].join("\n");
|
|
52
|
-
|
|
53
|
-
if (args.v) {
|
|
54
|
-
process.stdout.write(`${pkg.version}\n`);
|
|
55
|
-
process.exit(0);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!args._.length || args._.length > 2 || (args._[1] && isNaN(parseInt(args._[1])))) {
|
|
59
|
-
help();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
let host = args._[0];
|
|
63
|
-
const opts = {};
|
|
64
|
-
let port = parseInt(args._[1]);
|
|
65
|
-
let printed = false;
|
|
66
|
-
const rtts = [];
|
|
67
|
-
let stats;
|
|
68
|
-
|
|
69
|
-
if (typeof host !== "string") {
|
|
70
|
-
help();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// host:port syntax
|
|
74
|
-
const matches = /^(.+):(\d+)$/.exec(host);
|
|
75
|
-
if (matches && matches.length === 3 && !port) {
|
|
76
|
-
host = matches[1];
|
|
77
|
-
port = matches[2];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// url syntax
|
|
81
|
-
if (/.+:\/\/.+/.test(host)) {
|
|
82
|
-
const url = require("url").parse(host);
|
|
83
|
-
const proto = url.protocol.replace(":", "");
|
|
84
|
-
host = url.host;
|
|
85
|
-
port = url.port || require("port-numbers").getPort(proto).port;
|
|
86
|
-
if (!port) {
|
|
87
|
-
writeLine(chalk.red("ERROR:"), `Unknown protocol '${proto}'`);
|
|
88
|
-
process.exit(1);
|
|
89
|
-
}
|
|
90
|
-
if (!host) {
|
|
91
|
-
writeLine(chalk.red("ERROR:"), `Missing host in '${host}'`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!port) port = DEFAULT_PORT;
|
|
96
|
-
if (args.count || args.c) opts.count = parseInt(args.count || args.c);
|
|
97
|
-
if (args.interval || args.i) opts.interval = secondsToMs(args.interval || args.i);
|
|
98
|
-
if (args.timeout || args.t) opts.timeout = secondsToMs(args.timeout || args.t);
|
|
99
|
-
if (args.flood || args.f) opts.interval = 0;
|
|
100
|
-
if (args.C) chalk.enabled = false;
|
|
101
|
-
|
|
102
|
-
// Do a DNS lookup and start the connects
|
|
103
|
-
if (!net.isIP(host)) {
|
|
104
|
-
dns.lookup(host, (err, address) => {
|
|
105
|
-
if (!err) {
|
|
106
|
-
printStart(host, address, port);
|
|
107
|
-
run(host, port, opts);
|
|
108
|
-
} else {
|
|
109
|
-
if (err.code === "ENOTFOUND") writeLine(chalk.red("ERROR:"), `Host '${host}' not found`);
|
|
110
|
-
else writeLine(chalk.red("ERROR:"), err.code, err.syscall || "");
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
} else {
|
|
115
|
-
printStart(host, host, port);
|
|
116
|
-
run(host, port, opts);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function run(host, port, opts) {
|
|
120
|
-
const pie = tcpie(host, port, opts);
|
|
121
|
-
|
|
122
|
-
pie.on("error", (err, data) => {
|
|
123
|
-
stats = data;
|
|
124
|
-
writeLine(
|
|
125
|
-
chalk.red("error connecting to", `${data.target.host}:${data.target.port}`),
|
|
126
|
-
`seq=${data.sent}`,
|
|
127
|
-
`error=${chalk.red(err.code)}`
|
|
128
|
-
);
|
|
129
|
-
}).on("connect", data => {
|
|
130
|
-
stats = data;
|
|
131
|
-
rtts.push(data.rtt);
|
|
132
|
-
writeLine(
|
|
133
|
-
chalk.green("connected to", `${data.target.host}:${data.target.port}`),
|
|
134
|
-
`seq=${data.sent}`,
|
|
135
|
-
(data.socket.localPort !== undefined) ? `srcport=${data.socket.localPort}` : "",
|
|
136
|
-
`time=${colorRTT(data.rtt.toFixed(DIGITS_LINE))}`,
|
|
137
|
-
);
|
|
138
|
-
}).on("timeout", data => {
|
|
139
|
-
stats = data;
|
|
140
|
-
writeLine(
|
|
141
|
-
chalk.red("timeout connecting to", `${data.target.host}:${data.target.port}`),
|
|
142
|
-
`seq=${data.sent}`,
|
|
143
|
-
data.socket.localPort && `srcport=${data.socket.localPort}`
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
if (process.stdin.isTTY) {
|
|
148
|
-
process.stdin.setRawMode(true);
|
|
149
|
-
process.stdin.on("data", bytes => {
|
|
150
|
-
// http://nemesis.lonestar.org/reference/telecom/codes/ascii.html
|
|
151
|
-
const exitCodes = [
|
|
152
|
-
3, // SIGINT
|
|
153
|
-
4, // EOF
|
|
154
|
-
26, // SIGTSTP
|
|
155
|
-
28, // SIGQUIT
|
|
156
|
-
];
|
|
157
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
158
|
-
if (exitCodes.includes(bytes[i])) printEnd();
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
} else {
|
|
162
|
-
process.on("SIGINT", process.exit);
|
|
163
|
-
process.on("SIGQUIT", process.exit);
|
|
164
|
-
process.on("SIGTERM", process.exit);
|
|
165
|
-
process.on("SIGTSTP", process.exit);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
process.on("exit", printEnd);
|
|
169
|
-
pie.on("end", printEnd).start();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function printStart(host, address, port) {
|
|
173
|
-
writeLine(pkg.name.toUpperCase(), host, `(${address})`, "port", String(port));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function printEnd() {
|
|
177
|
-
let sum = 0, min = Infinity, max = 0, avg, dev;
|
|
178
|
-
|
|
179
|
-
if (printed) process.exit(stats.success === 0 && 1 || 0);
|
|
180
|
-
|
|
181
|
-
if (stats && stats.sent > 0) {
|
|
182
|
-
rtts.forEach(rtt => {
|
|
183
|
-
if (rtt <= min) min = rtt.toFixed(DIGITS_STATS);
|
|
184
|
-
if (rtt >= max) max = rtt.toFixed(DIGITS_STATS);
|
|
185
|
-
sum += rtt;
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
avg = (sum / rtts.length).toFixed(DIGITS_STATS);
|
|
189
|
-
dev = stdev(rtts).toFixed(DIGITS_STATS);
|
|
190
|
-
|
|
191
|
-
if (min === Infinity) min = "0";
|
|
192
|
-
if (isNaN(avg)) avg = "0";
|
|
193
|
-
|
|
194
|
-
printed = true;
|
|
195
|
-
|
|
196
|
-
writeLine(
|
|
197
|
-
"\n---", host, `${pkg.name} statistics`, "---",
|
|
198
|
-
`\n${stats.sent}`, "handshakes attempted,", stats.success || "0", "succeeded,",
|
|
199
|
-
`${((stats.failed / stats.sent) * 100).toFixed(DIGITS_PERC)}% failed`,
|
|
200
|
-
"\nrtt min/avg/max/stdev =", `${min}/${avg}/${max}/${dev}`, "ms"
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
process.exit(stats.success === 0 && 1 || 0);
|
|
204
|
-
} else {
|
|
205
|
-
process.exit(1);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function colorRTT(rtt) {
|
|
210
|
-
return `${chalk[rtt >= 150 ? "red" : rtt >= 75 ? "yellow" : "green"](rtt)} ms`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function writeLine(...arg) {
|
|
214
|
-
arg = arg.filter(string => Boolean(string));
|
|
215
|
-
if ((args.timeout || args.T) && arg[0][0] !== "\n") arg.unshift(timestamp());
|
|
216
|
-
arg.push("\n");
|
|
217
|
-
const stream = (process.stdout._type === "pipe" && printed) ? process.stderr : process.stdout;
|
|
218
|
-
stream.write(arg.join(" "));
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function help() {
|
|
222
|
-
process.stdout.write(usage);
|
|
223
|
-
process.exit(1);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function secondsToMs(s) {
|
|
227
|
-
return (parseFloat(s) * 1000);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function timestamp() {
|
|
231
|
-
const now = new Date();
|
|
232
|
-
const year = now.getFullYear();
|
|
233
|
-
let month = now.getMonth() + 1;
|
|
234
|
-
let day = now.getDate();
|
|
235
|
-
let hrs = now.getHours();
|
|
236
|
-
let mins = now.getMinutes();
|
|
237
|
-
let secs = now.getSeconds();
|
|
238
|
-
|
|
239
|
-
if (month < 10) month = `0${month}`;
|
|
240
|
-
if (day < 10) day = `0${day}`;
|
|
241
|
-
if (hrs < 10) hrs = `0${hrs}`;
|
|
242
|
-
if (mins < 10) mins = `0${mins}`;
|
|
243
|
-
if (secs < 10) secs = `0${secs}`;
|
|
244
|
-
return `${year}-${month}-${day} ${hrs}:${mins}:${secs}`;
|
|
245
|
-
}
|