is-localhost-ip 2.0.0 → 3.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 +42 -20
- package/index.js +38 -31
- package/package.json +31 -44
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
[](https://codecov.io/gh/tinovyatkin/is-localhost-ip)
|
|
1
|
+
[](https://codecov.io/gh/tinovyatkin/is-localhost-ip)
|
|
2
|
+

|
|
2
3
|
|
|
3
4
|
# is-localhost-ip
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Zero-dependency Node.js utility that checks whether a hostname or IPv4/IPv6 address refers to the local machine.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
This package aims to be strict and comprehensive:
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
- Validates input as an IP address or a syntactically valid hostname (including bracketed IPv6).
|
|
11
|
+
- Treats private/loopback/link-local ranges as local.
|
|
12
|
+
- Optionally verifies the address exists on the current machine by attempting to bind to it.
|
|
13
|
+
- Falls back to DNS resolution, so it works with hostnames mapped in `/etc/hosts` or a local resolver.
|
|
10
14
|
|
|
11
15
|
## Installation
|
|
12
16
|
|
|
@@ -14,37 +18,55 @@ All this in just _~100 lines of code_ without external dependencies.
|
|
|
14
18
|
npm i is-localhost-ip
|
|
15
19
|
# or
|
|
16
20
|
yarn add is-localhost-ip
|
|
21
|
+
# or
|
|
22
|
+
pnpm add is-localhost-ip
|
|
17
23
|
```
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
Requires Node.js `>=18`.
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
20
28
|
|
|
21
29
|
```js
|
|
22
|
-
const isLocalhost = require(
|
|
30
|
+
const isLocalhost = require("is-localhost-ip");
|
|
23
31
|
|
|
24
32
|
(async () => {
|
|
25
|
-
await isLocalhost(
|
|
26
|
-
await isLocalhost(
|
|
27
|
-
await isLocalhost(
|
|
28
|
-
await isLocalhost(
|
|
29
|
-
await isLocalhost(
|
|
30
|
-
|
|
31
|
-
await isLocalhost(
|
|
32
|
-
await isLocalhost(
|
|
33
|
-
await isLocalhost(
|
|
33
|
+
await isLocalhost("127.0.0.1"); // true
|
|
34
|
+
await isLocalhost("::ffff:127.0.0.1"); // true
|
|
35
|
+
await isLocalhost("192.168.0.12"); // true
|
|
36
|
+
await isLocalhost("192.168.0.12", true); // true only if an interface has this address
|
|
37
|
+
await isLocalhost("184.55.123.2"); // false
|
|
38
|
+
|
|
39
|
+
await isLocalhost("tino.local"); // true if it resolves to a local address
|
|
40
|
+
await isLocalhost("localhost"); // true
|
|
41
|
+
await isLocalhost("microsoft.com"); // false
|
|
34
42
|
})();
|
|
35
43
|
```
|
|
36
44
|
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
### `isLocalhost(ipOrHostname, canBind?)`
|
|
48
|
+
|
|
49
|
+
Returns a `Promise<boolean>`.
|
|
50
|
+
|
|
51
|
+
- `ipOrHostname` (`string`): IP address (v4/v6) or a hostname.
|
|
52
|
+
- `canBind` (`boolean`, default `false`): when `true`, additionally checks that the local machine can bind to the
|
|
53
|
+
address (i.e., it is configured on a local interface).
|
|
54
|
+
|
|
55
|
+
The function throws for invalid inputs (non-string values or syntactically invalid hostnames).
|
|
56
|
+
|
|
37
57
|
## Caveats
|
|
38
58
|
|
|
39
|
-
|
|
59
|
+
Internationalized domain names (IDNs) are not supported. If you need IDNs, use
|
|
60
|
+
[Punycode.js](https://github.com/bestiejs/punycode.js) (or another punycode implementation) to convert the input
|
|
61
|
+
to ASCII before calling this function:
|
|
40
62
|
|
|
41
63
|
```js
|
|
42
|
-
const isLocalhost = require(
|
|
43
|
-
const punycode = require(
|
|
64
|
+
const isLocalhost = require("is-localhost-ip");
|
|
65
|
+
const punycode = require("punycode");
|
|
44
66
|
|
|
45
67
|
(async () => {
|
|
46
|
-
await isLocalhost(punycode.toASCII(
|
|
47
|
-
await isLocalhost(punycode.toASCII(
|
|
68
|
+
await isLocalhost(punycode.toASCII("свобода.рф")); // false
|
|
69
|
+
await isLocalhost(punycode.toASCII("私の.家")); // true
|
|
48
70
|
})();
|
|
49
71
|
```
|
|
50
72
|
|
package/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
const { isIP, isIPv4 } = require(
|
|
4
|
-
const { createSocket } = require(
|
|
5
|
-
const { ADDRCONFIG } = require(
|
|
6
|
-
const { lookup } = require(
|
|
3
|
+
const { isIP, isIPv4 } = require("node:net");
|
|
4
|
+
const { createSocket } = require("node:dgram");
|
|
5
|
+
const { ADDRCONFIG } = require("node:dns");
|
|
6
|
+
const { lookup } = require("node:dns").promises;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Addresses reserved for private networks
|
|
@@ -22,15 +22,13 @@ const IP_RANGES = [
|
|
|
22
22
|
// 192.168.0.0 - 192.168.255.255
|
|
23
23
|
/^(:{2}f{4}:)?192\.168(?:\.\d{1,3}){2}$/,
|
|
24
24
|
// fc00::/7
|
|
25
|
-
/^f[cd][\da-f]{2}(::1
|
|
25
|
+
/^f[cd][\da-f]{2}(::1?$|:[\da-f]{1,4}){1,7}$/,
|
|
26
26
|
// fe80::/10
|
|
27
|
-
/^fe[89ab][\da-f](::1
|
|
27
|
+
/^fe[89ab][\da-f](::1?$|:[\da-f]{1,4}){1,7}$/,
|
|
28
28
|
];
|
|
29
29
|
|
|
30
30
|
// Concat all RegExes from above into one
|
|
31
|
-
const IP_TESTER_RE = new RegExp(
|
|
32
|
-
`^(${IP_RANGES.map((re) => re.source).join('|')})$`,
|
|
33
|
-
);
|
|
31
|
+
const IP_TESTER_RE = new RegExp(`^(${IP_RANGES.map((re) => re.source).join("|")})$`);
|
|
34
32
|
|
|
35
33
|
/**
|
|
36
34
|
* Syntax validation RegExp for possible valid host names. Permits underscore.
|
|
@@ -47,12 +45,12 @@ const VALID_HOSTNAME =
|
|
|
47
45
|
* @returns {Promise<boolean>}
|
|
48
46
|
*/
|
|
49
47
|
async function canBindToIp(ip) {
|
|
50
|
-
const socket = createSocket(isIPv4(ip) ?
|
|
48
|
+
const socket = createSocket(isIPv4(ip) ? "udp4" : "udp6");
|
|
51
49
|
return new Promise((resolve) => {
|
|
52
50
|
try {
|
|
53
51
|
socket
|
|
54
|
-
.once(
|
|
55
|
-
.once(
|
|
52
|
+
.once("error", () => socket.close(() => resolve(false)))
|
|
53
|
+
.once("listening", () => socket.close(() => resolve(true)))
|
|
56
54
|
.unref()
|
|
57
55
|
.bind(0, ip);
|
|
58
56
|
} catch {
|
|
@@ -69,31 +67,40 @@ async function canBindToIp(ip) {
|
|
|
69
67
|
* @returns {Promise.<boolean>} - true, if given strings is a local IP address or DNS names that resolves to local IP
|
|
70
68
|
*/
|
|
71
69
|
async function isLocalhost(ipOrHostname, canBind = false) {
|
|
72
|
-
if (typeof ipOrHostname !==
|
|
70
|
+
if (typeof ipOrHostname !== "string") {
|
|
71
|
+
throw new TypeError("Invalid ip or hostname provided");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Removes [ and ] around ipv6 hostnames
|
|
75
|
+
const normalizedIpOrHostname = ipOrHostname.replaceAll(/^\[|\]$/g, "");
|
|
73
76
|
|
|
74
77
|
// Check if given string is an IP address
|
|
75
|
-
if (isIP(
|
|
76
|
-
if (IP_TESTER_RE.test(
|
|
77
|
-
return canBindToIp(
|
|
78
|
+
if (isIP(normalizedIpOrHostname)) {
|
|
79
|
+
if (IP_TESTER_RE.test(normalizedIpOrHostname) && !canBind) return true;
|
|
80
|
+
return canBindToIp(normalizedIpOrHostname);
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
// May it be a hostname?
|
|
81
|
-
if (!VALID_HOSTNAME.test(
|
|
84
|
+
if (!VALID_HOSTNAME.test(normalizedIpOrHostname)) {
|
|
85
|
+
throw new Error("Invalid ip or hostname provided");
|
|
86
|
+
}
|
|
82
87
|
|
|
83
88
|
// it's a DNS name
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
const addresses = await lookup(normalizedIpOrHostname, {
|
|
90
|
+
all: true,
|
|
91
|
+
family: 0,
|
|
92
|
+
verbatim: true,
|
|
93
|
+
hints: ADDRCONFIG,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!Array.isArray(addresses)) {
|
|
97
|
+
throw new TypeError("DNS Lookup failed.");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const { address } of addresses) {
|
|
101
|
+
if (await isLocalhost(address, canBind)) return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
97
104
|
return false;
|
|
98
105
|
}
|
|
99
106
|
|
package/package.json
CHANGED
|
@@ -1,59 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "is-localhost-ip",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Checks whether given DNS name or IPv4/IPv6 address belongs to a local machine",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"check-localhost",
|
|
7
|
+
"dns",
|
|
8
|
+
"ip",
|
|
9
|
+
"is-local-ip",
|
|
10
|
+
"is-localhost",
|
|
11
|
+
"is-loopback",
|
|
12
|
+
"localhost"
|
|
9
13
|
],
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"test": "jest --coverage --verbose",
|
|
16
|
-
"lint": "eslint ."
|
|
14
|
+
"homepage": "https://github.com/tinovyatkin/is-localhost-ip#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/tinovyatkin/is-localhost-ip/issues"
|
|
17
17
|
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Konstantin Vyatkin <tino@vtkn.io>",
|
|
18
20
|
"repository": {
|
|
19
21
|
"type": "git",
|
|
20
22
|
"url": "git+https://github.com/tinovyatkin/is-localhost-ip.git"
|
|
21
23
|
},
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"check-localhost",
|
|
26
|
-
"is-loopback",
|
|
27
|
-
"is-local-ip",
|
|
28
|
-
"ip",
|
|
29
|
-
"dns"
|
|
24
|
+
"files": [
|
|
25
|
+
"index.d.ts",
|
|
26
|
+
"index.js"
|
|
30
27
|
],
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
28
|
+
"main": "index.js",
|
|
29
|
+
"browser": false,
|
|
30
|
+
"types": "index.d.ts",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "node --test --experimental-test-module-mocks --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage/lcov.info __tests__/*.test.js",
|
|
33
|
+
"lint": "oxlint --fix --fix-suggestions",
|
|
34
|
+
"format": "oxfmt --write .",
|
|
35
|
+
"prepare": "lefthook install"
|
|
35
36
|
},
|
|
36
|
-
"homepage": "https://github.com/tinovyatkin/is-localhost-ip#readme",
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"eslint-plugin-node": "11.1.0",
|
|
43
|
-
"eslint-plugin-prettier": "4.2.1",
|
|
44
|
-
"eslint-plugin-regexp": "^1.8.0",
|
|
45
|
-
"jest": "28.1.3",
|
|
46
|
-
"prettier": "2.7.1"
|
|
38
|
+
"@types/node": "22.15.21",
|
|
39
|
+
"lefthook": "^2.0.15",
|
|
40
|
+
"oxfmt": "^0.24.0",
|
|
41
|
+
"oxlint": "^1.39.0"
|
|
47
42
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"text",
|
|
51
|
-
"json",
|
|
52
|
-
"cobertura",
|
|
53
|
-
"lcov"
|
|
54
|
-
],
|
|
55
|
-
"coverageProvider": "v8",
|
|
56
|
-
"testEnvironment": "node",
|
|
57
|
-
"collectCoverage": true
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
58
45
|
}
|
|
59
46
|
}
|