@k03mad/dns-leak 2.0.0 → 3.0.0

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 CHANGED
@@ -1,8 +1,10 @@
1
1
  # DNS leak test
2
2
 
3
- Based on:\
4
- — [ipleak.net](https://ipleak.net) ([API](https://airvpn.org/forums/topic/14737-api))\
5
- — [ipwhois.io](https://ipwhois.io) ([API](https://ipwhois.io/documentation))
3
+ Using API/tools:\
4
+ — [ipleak.net](https://airvpn.org/forums/topic/14737-api)\
5
+ — [ipwhois.io](https://ipwhois.io/documentation)\
6
+ — [cloudping.cloud](https://www.cloudping.cloud/cdn)\
7
+ — [nextdns.io](https://test.nextdns.io/)
6
8
 
7
9
  ## Global
8
10
 
@@ -19,21 +21,6 @@ npm i @k03mad/dns-leak
19
21
  ```
20
22
 
21
23
  ```js
22
- import {IPLeak, IPWhois} from '@k03mad/dns-leak';
23
-
24
- // default params (details at the ./app/api folder)
25
- const LeakApi = new IPLeak();
26
- const WhoisApi = new IPWhois();
27
-
28
- // get current external ip info
29
- await WhoisApi.getIpInfo();
30
- await LeakApi.getIpInfo();
31
- // get other ip info
32
- await WhoisApi.getIpInfo({ip: '8.8.8.8'});
33
- await LeakApi.getIpInfo({ip: '8.8.8.8'});
34
-
35
- // dns leak check with one request (one request — fewer dns ips)
36
- await LeakApi.getDnsInfoOnce();
37
- // dns leak check with multi requests
38
- await LeakApi.getDnsInfoMulti();
24
+ import {CloudPing, IPLeak, IPWhois, NextDNS} from './api/_index.js';
25
+ // details at ./app/api folder
39
26
  ```
@@ -0,0 +1,66 @@
1
+ import {request} from '@k03mad/request';
2
+
3
+ /** */
4
+ export default class CloudPing {
5
+
6
+ /**
7
+ * @param {object} [opts]
8
+ * @param {number} [opts.requestsRps] parallel requests rps
9
+ */
10
+ constructor({
11
+ requestsRps = 2,
12
+ } = {}) {
13
+ this._requestsRps = requestsRps;
14
+ }
15
+
16
+ /** */
17
+ get _endpoints() {
18
+ return {
19
+
20
+ /** */
21
+ edge: () => 'https://edge.feitsui.com/',
22
+
23
+ /** */
24
+ locations: () => 'https://www.cloudping.cloud/cloudfront-edge-locations.json',
25
+ };
26
+ }
27
+
28
+ /**
29
+ * @returns {Promise<string>}
30
+ */
31
+ async getCurrentIataCode() {
32
+ const testEndpoint = this._endpoints.edge();
33
+
34
+ const {headers} = await request(testEndpoint, {}, {
35
+ rps: this._requestsRps,
36
+ });
37
+
38
+ return headers['x-amz-cf-pop'];
39
+ }
40
+
41
+ /**
42
+ * @returns {Promise<object>}
43
+ */
44
+ async getAllLocations() {
45
+ const locationsEndpoint = this._endpoints.locations();
46
+
47
+ const {body} = await request(locationsEndpoint, {}, {
48
+ rps: this._requestsRps,
49
+ });
50
+
51
+ return body.nodes;
52
+ }
53
+
54
+ /**
55
+ * @returns {Promise<object>}
56
+ */
57
+ async getCurrentLocation() {
58
+ const [iata, locations] = await Promise.all([
59
+ this.getCurrentIataCode(),
60
+ this.getAllLocations(),
61
+ ]);
62
+
63
+ return {...locations[iata.replace(/\d.+/, '')], iata};
64
+ }
65
+
66
+ }
package/app/api/IPLeak.js CHANGED
@@ -11,29 +11,26 @@ export default class IPLeak {
11
11
  /**
12
12
  * @param {object} [opts]
13
13
  * @param {number} [opts.dnsRequestsCount] dns leak multi requests count with one session
14
- * @param {number} [opts.dnsRequestsRps] dns leak requests rps
15
14
  * @param {number} [opts.dnsRequestsWaitBeforeLastMs] dns leak multi requests wait before the last request (with all ips gathered)
16
15
  * @param {number} [opts.dnsSessionStringLength] dns leak session string length, only works with 40 characters for now
17
16
  * @param {number} [opts.dnsUniqStringLength] dns leak unique string length for subdomain
18
17
  * @param {number} [opts.ipRequestsCacheExpireMs] ip info requests cache ttl ms for same ip
19
- * @param {number} [opts.ipRequestsRps] ip info requests rps
18
+ * @param {number} [opts.requestsRps] parallel requests rps
20
19
  */
21
20
  constructor({
22
21
  dnsRequestsCount = 30,
23
- dnsRequestsRps = 2,
24
22
  dnsRequestsWaitBeforeLastMs = 2000,
25
23
  dnsSessionStringLength = 40,
26
24
  dnsUniqStringLength = 20,
27
25
  ipRequestsCacheExpireMs = 3_600_000,
28
- ipRequestsRps = 2,
26
+ requestsRps = 2,
29
27
  } = {}) {
30
28
  this._dnsRequestsCount = dnsRequestsCount;
31
- this._dnsRequestsRps = dnsRequestsRps;
32
29
  this._dnsRequestsWaitBeforeLastMs = dnsRequestsWaitBeforeLastMs;
33
30
  this._dnsSessionStringLength = dnsSessionStringLength;
34
31
  this._dnsUniqStringLength = dnsUniqStringLength;
35
32
  this._ipRequestsCacheExpireMs = ipRequestsCacheExpireMs;
36
- this._ipRequestsRps = ipRequestsRps;
33
+ this._requestsRps = requestsRps;
37
34
  }
38
35
 
39
36
  /** */
@@ -71,7 +68,7 @@ export default class IPLeak {
71
68
 
72
69
  const {body} = await requestCache(ipEndpoint, {}, {
73
70
  expire: this._ipRequestsCacheExpireMs,
74
- rps: this._ipRequestsRps,
71
+ rps: this._requestsRps,
75
72
  });
76
73
 
77
74
  return body;
@@ -86,7 +83,11 @@ export default class IPLeak {
86
83
  async getDnsInfoOnce({session = this._dnsSessionString, uniqString = this._dnsUniqString} = {}) {
87
84
  const dnsEndpoint = this._endpoints.dns(session, uniqString);
88
85
 
89
- const {body} = await request(dnsEndpoint, {}, {queueBy: session, rps: this._dnsRequestsRps});
86
+ const {body} = await request(dnsEndpoint, {}, {
87
+ queueBy: session,
88
+ rps: this._requestsRps,
89
+ });
90
+
90
91
  return body;
91
92
  }
92
93
 
@@ -97,7 +98,7 @@ export default class IPLeak {
97
98
  * @returns {Promise<object>}
98
99
  */
99
100
  async getDnsInfoMulti({isSpinnerEnabled, session = this._dnsSessionString} = {}) {
100
- const spinnerName = 'dnsReq';
101
+ const spinnerName = 'DNS info';
101
102
  const arrayFromLen = Array.from({length: this._dnsRequestsCount - 1});
102
103
 
103
104
  spinner.start(spinnerName, isSpinnerEnabled);
@@ -107,7 +108,6 @@ export default class IPLeak {
107
108
  spinner.count(spinnerName, this._dnsRequestsCount);
108
109
  }));
109
110
 
110
- spinner.text(spinnerName, 'Wait for last request');
111
111
  await sleep(this._dnsRequestsWaitBeforeLastMs);
112
112
  const info = await this.getDnsInfoOnce({session});
113
113
 
@@ -6,14 +6,14 @@ export default class IPWhois {
6
6
  /**
7
7
  * @param {object} [opts]
8
8
  * @param {number} [opts.ipRequestsCacheExpireMs] ip info requests cache ttl ms for same ip
9
- * @param {number} [opts.ipRequestsRps] ip info requests rps
9
+ * @param {number} [opts.requestsRps] parallel requests rps
10
10
  */
11
11
  constructor({
12
12
  ipRequestsCacheExpireMs = 3_600_000,
13
- ipRequestsRps = 2,
13
+ requestsRps = 2,
14
14
  } = {}) {
15
15
  this._ipRequestsCacheExpireMs = ipRequestsCacheExpireMs;
16
- this._ipRequestsRps = ipRequestsRps;
16
+ this._requestsRps = requestsRps;
17
17
  }
18
18
 
19
19
  /** */
@@ -35,7 +35,7 @@ export default class IPWhois {
35
35
 
36
36
  const {body} = await requestCache(ipEndpoint, {}, {
37
37
  expire: this._ipRequestsCacheExpireMs,
38
- rps: this._ipRequestsRps,
38
+ rps: this._requestsRps,
39
39
  });
40
40
 
41
41
  return body;
@@ -0,0 +1,38 @@
1
+ import {request} from '@k03mad/request';
2
+
3
+ /** */
4
+ export default class NextDNS {
5
+
6
+ /**
7
+ * @param {object} [opts]
8
+ * @param {number} [opts.requestsRps] parallel requests rps
9
+ */
10
+ constructor({
11
+ requestsRps = 2,
12
+ } = {}) {
13
+ this._requestsRps = requestsRps;
14
+ }
15
+
16
+ /** */
17
+ get _endpoints() {
18
+ return {
19
+
20
+ /** */
21
+ test: () => 'https://test.nextdns.io/',
22
+ };
23
+ }
24
+
25
+ /**
26
+ * @returns {Promise<object>}
27
+ */
28
+ async getTest() {
29
+ const testEndpoint = this._endpoints.test();
30
+
31
+ const {body} = await request(testEndpoint, {}, {
32
+ rps: this._requestsRps,
33
+ });
34
+
35
+ return body;
36
+ }
37
+
38
+ }
@@ -0,0 +1,4 @@
1
+ export {default as CloudPing} from './CloudPing.js';
2
+ export {default as IPLeak} from './IPLeak.js';
3
+ export {default as IPWhois} from './IPWhois.js';
4
+ export {default as NextDNS} from './NextDNS.js';
@@ -1,6 +1,6 @@
1
1
  import ora from 'ora';
2
2
 
3
- import {bar} from './text.js';
3
+ import {bar, info} from './text.js';
4
4
 
5
5
  export const spinner = {};
6
6
 
@@ -26,7 +26,7 @@ export const start = (name, active) => {
26
26
  * @param {string} msg
27
27
  */
28
28
  export const text = (name, msg) => {
29
- spinner[name].instance.text = bar(msg);
29
+ spinner[name].instance.text = `${info(name)} ${bar(msg)}`;
30
30
  };
31
31
 
32
32
  /**
@@ -1,47 +1,71 @@
1
1
  import chalk from 'chalk';
2
+ import clm from 'country-locale-map';
2
3
 
3
- const {bgBlackBright, blue, gray, green, magenta, yellow} = chalk;
4
+ const {blue, gray, green, magenta, yellow} = chalk;
5
+ const SEPARATOR = ' :: ';
4
6
 
5
7
  /**
6
- * @param {any} msg
8
+ * @param {string} msg
7
9
  */
8
- export const header = msg => bgBlackBright(magenta(` ${msg} `));
10
+ export const header = msg => magenta.bold.bgBlackBright(` ${msg}`.padEnd(25, ' '));
9
11
 
10
12
  /**
11
- * @param {any} msg
13
+ * @param {string} msg
12
14
  */
13
15
  export const bar = msg => yellow(msg);
14
16
 
17
+ /**
18
+ * @param {string} msg
19
+ */
20
+ export const info = msg => gray(msg);
21
+
22
+ /**
23
+ * @param {string} msg
24
+ */
25
+ export const org = msg => green(msg);
26
+
27
+ /**
28
+ * @param {string} msg
29
+ */
30
+ export const address = msg => blue(msg);
31
+
15
32
  /**
16
33
  * @param {object} [ipInfo]
34
+ * @param {object} [ipInfo.connection]
35
+ * @param {object} [ipInfo.flag]
17
36
  * @param {string} [ipInfo.city]
18
37
  * @param {string} [ipInfo.country]
19
38
  * @param {string} [ipInfo.ip]
20
39
  * @param {string} [ipInfo.region]
21
- * @param {object} [ipInfo.flag]
22
- * @param {object} [ipInfo.connection]
23
40
  */
24
- export const formatIpInfo = ({city, connection, country, flag, ip, region} = {}) => {
41
+ export const formatIpInfo = ({
42
+ city,
43
+ connection,
44
+ country,
45
+ flag,
46
+ ip,
47
+ region,
48
+ } = {}) => {
25
49
  let output = '';
26
50
 
27
51
  if (ip) {
28
- output += `${blue(ip)}\n`;
52
+ output += `${address(ip)}\n`;
29
53
  }
30
54
 
31
55
  if (connection?.org) {
32
- output += `${green(connection.org)} `;
56
+ output += `${org(connection.org)} `;
33
57
  }
34
58
 
35
59
  if (connection?.isp && !connection?.org?.includes(connection?.isp)) {
36
60
  if (connection?.org) {
37
- output += green('/ ');
61
+ output += org('/ ');
38
62
  }
39
63
 
40
- output += `${green(connection.isp)} `;
64
+ output += `${org(connection.isp)} `;
41
65
  }
42
66
 
43
67
  if (connection?.domain) {
44
- output += gray(`(${connection.domain})`);
68
+ output += info(`(${connection.domain})`);
45
69
  }
46
70
 
47
71
  output += '\n';
@@ -56,7 +80,37 @@ export const formatIpInfo = ({city, connection, country, flag, ip, region} = {})
56
80
  region,
57
81
  city,
58
82
  ]),
59
- ].filter(Boolean).join(' :: ');
83
+ ].filter(Boolean).join(SEPARATOR);
84
+
85
+ return output;
86
+ };
87
+
88
+ /**
89
+ * @param {object} [opts]
90
+ * @param {string} [opts.iata]
91
+ * @param {string} [opts.city]
92
+ * @param {string} [opts.country]
93
+ */
94
+ export const formatLocationInfo = ({city, country, iata}) => {
95
+ let output = '';
96
+
97
+ if (iata) {
98
+ output += `${org(iata)}\n`;
99
+ }
100
+
101
+ if (country) {
102
+ const {emoji} = clm.getCountryByName(country);
103
+
104
+ if (emoji) {
105
+ output += `${emoji} `;
106
+ }
107
+
108
+ output += country;
109
+ }
110
+
111
+ if (city) {
112
+ output += SEPARATOR + city;
113
+ }
60
114
 
61
115
  return output;
62
116
  };
package/app/run.js CHANGED
@@ -1,37 +1,61 @@
1
1
  #!/usr/bin/env node
2
- import {IPLeak, IPWhois} from './api/index.js';
2
+ import {CloudPing, IPLeak, IPWhois, NextDNS} from './api/_index.js';
3
3
  import {log} from './helpers/log.js';
4
4
  import * as spinner from './helpers/spinner.js';
5
- import {formatIpInfo, header} from './helpers/text.js';
5
+ import {formatIpInfo, formatLocationInfo, header} from './helpers/text.js';
6
6
 
7
7
  const LeakApi = new IPLeak();
8
+ const NextApi = new NextDNS();
8
9
  const WhoisApi = new IPWhois();
10
+ const CloudPingApi = new CloudPing();
9
11
 
10
- const currentIpInfo = await WhoisApi.getIpInfo();
12
+ const [leak, next, whois, location] = await Promise.all([
13
+ LeakApi.getDnsInfoMulti({isSpinnerEnabled: true}),
14
+ NextApi.getTest(),
15
+ WhoisApi.getIpInfo(),
16
+ CloudPingApi.getCurrentLocation(),
17
+ ]);
18
+
19
+ const spinnerName = 'IP info';
20
+ spinner.start(spinnerName, true);
21
+
22
+ const dnsIps = [...new Set([...Object.keys(leak.ip), next.resolver])];
23
+
24
+ const dnsData = await Promise.all(dnsIps.map(async ip => {
25
+ const data = await WhoisApi.getIpInfo({ip});
26
+ spinner.count(spinnerName, dnsIps.length);
27
+ return data;
28
+ }));
29
+
30
+ spinner.stop(spinnerName);
11
31
 
12
32
  log(
13
33
  '',
14
34
  header('IP'),
15
35
  '',
16
- formatIpInfo(currentIpInfo),
36
+ formatIpInfo(whois),
17
37
  '',
18
38
  header('DNS'),
19
39
  '',
40
+ ...dnsData
41
+ .sort((a, b) => a?.ip?.localeCompare(b?.ip))
42
+ .flatMap(data => [formatIpInfo(data), '']),
20
43
  );
21
44
 
22
- const dnsInfo = await LeakApi.getDnsInfoMulti({isSpinnerEnabled: true});
23
- const dnsIps = [...new Set(Object.keys(dnsInfo.ip))];
45
+ if (next.ecs) {
46
+ const data = await WhoisApi.getIpInfo({ip: next.ecs.replace(/\/.+/, '')});
47
+ data.ip += ` (${next.ecs})`;
24
48
 
25
- spinner.start(currentIpInfo.ip, true);
49
+ log(
50
+ header('DNS ECS'),
51
+ '',
52
+ formatIpInfo(data),
53
+ '',
54
+ );
55
+ }
26
56
 
27
- const dnsData = await Promise.all(dnsIps.map(async ip => {
28
- const data = await WhoisApi.getIpInfo({ip});
29
- spinner.count(currentIpInfo.ip, dnsIps.length);
30
- return data;
31
- }));
32
-
33
- spinner.stop(currentIpInfo.ip);
34
-
35
- dnsData
36
- .sort((a, b) => a?.ip?.localeCompare(b?.ip))
37
- .forEach(data => log(formatIpInfo(data), ''));
57
+ log(
58
+ header('CLOUDFRONT CDN'),
59
+ '',
60
+ formatLocationInfo(location),
61
+ );
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@k03mad/dns-leak",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "DNS leak test",
5
5
  "maintainers": [
6
6
  "Kirill Molchanov <k03.mad@gmail.com"
7
7
  ],
8
- "main": "app/api/index.js",
8
+ "main": "app/api/_index.js",
9
9
  "bin": "app/run.js",
10
10
  "repository": "k03mad/dns-leak",
11
11
  "license": "MIT",
@@ -16,6 +16,7 @@
16
16
  "dependencies": {
17
17
  "@k03mad/request": "3.2.0",
18
18
  "chalk": "5.3.0",
19
+ "country-locale-map": "1.8.15",
19
20
  "nanoid": "5.0.2",
20
21
  "nanoid-dictionary": "5.0.0-beta.1",
21
22
  "ora": "7.0.1"
package/app/api/index.js DELETED
@@ -1,2 +0,0 @@
1
- export {default as IPLeak} from './IPLeak.js';
2
- export {default as IPWhois} from './IPWhois.js';