mirror of
https://github.com/remnawave/asn-index.git
synced 2026-05-13 04:09:06 +00:00
init
This commit is contained in:
commit
814dc63735
9 changed files with 668 additions and 0 deletions
61
.github/workflows/build.yml
vendored
Normal file
61
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
name: Build ASN Index
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build asn-prefixes.json
|
||||
run: bash scripts/build.sh
|
||||
|
||||
- name: Build LMDB database
|
||||
run: node scripts/build-lmdb.mjs
|
||||
|
||||
- name: Print stats
|
||||
run: |
|
||||
echo "=== ASN Index Stats ==="
|
||||
ASN_COUNT=$(jq 'keys | length' ./dist/asn-prefixes.json)
|
||||
echo "ASNs indexed: ${ASN_COUNT}"
|
||||
echo ""
|
||||
echo "Artifact sizes:"
|
||||
ls -lh ./dist/asn-prefixes.json ./dist/asn-prefixes-lmdb.tar.gz
|
||||
|
||||
- name: Force-update latest tag
|
||||
run: |
|
||||
git tag -f latest
|
||||
git push origin latest --force
|
||||
|
||||
- name: Create or update release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: latest
|
||||
name: Latest ASN Index
|
||||
body: |
|
||||
Auto-generated ASN prefix index, updated daily from [ipverse/as-ip-blocks](https://github.com/ipverse/as-ip-blocks).
|
||||
|
||||
**Last updated:** ${{ github.run_id }} — see workflow run for details.
|
||||
files: |
|
||||
./dist/asn-prefixes.json
|
||||
./dist/asn-prefixes-lmdb.tar.gz
|
||||
make_latest: true
|
||||
prerelease: false
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules/
|
||||
dist/
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Remnawave
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
83
README.md
Normal file
83
README.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# asn-index
|
||||
|
||||
Daily-updated index of ASN → IP prefix mappings, available in JSON and LMDB formats. Built from [ipverse/as-ip-blocks](https://github.com/ipverse/as-ip-blocks) and published as GitHub Release assets.
|
||||
|
||||
## Downloads
|
||||
|
||||
| Format | URL |
|
||||
|--------|-----|
|
||||
| JSON | https://github.com/remnawave/asn-index/releases/latest/download/asn-prefixes.json |
|
||||
| LMDB (tar.gz) | https://github.com/remnawave/asn-index/releases/latest/download/asn-prefixes-lmdb.tar.gz |
|
||||
|
||||
## Data format
|
||||
|
||||
### asn-prefixes.json
|
||||
|
||||
A single JSON object where each key is an ASN number (as a string) and the value contains IPv4 and IPv6 prefix lists:
|
||||
|
||||
```json
|
||||
{
|
||||
"13335": {
|
||||
"ipv4": ["1.0.0.0/24", "1.1.1.0/24"],
|
||||
"ipv6": ["2400:cb00::/32", "2606:4700::/32"]
|
||||
},
|
||||
"15169": {
|
||||
"ipv4": ["8.8.8.0/24"],
|
||||
"ipv6": ["2001:4860::/32"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### asn-prefixes.lmdb
|
||||
|
||||
An LMDB database with:
|
||||
- **Key**: ASN number (`number`)
|
||||
- **Value**: `{ ipv4: string[], ipv6: string[] }` (MessagePack-serialized via lmdb-js)
|
||||
|
||||
## Usage
|
||||
|
||||
### JSON
|
||||
|
||||
```js
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const data = JSON.parse(readFileSync('asn-prefixes.json', 'utf8'));
|
||||
|
||||
// Direct object lookup
|
||||
const cloudflare = data['13335'];
|
||||
console.log(cloudflare.ipv4); // ["1.0.0.0/24", ...]
|
||||
|
||||
// Convert to Map for repeated lookups
|
||||
const asnMap = new Map(Object.entries(data));
|
||||
console.log(asnMap.get('13335').ipv6);
|
||||
```
|
||||
|
||||
### LMDB
|
||||
|
||||
```js
|
||||
import { open } from 'lmdb';
|
||||
|
||||
// Extract asn-prefixes-lmdb.tar.gz first, then:
|
||||
const db = open({
|
||||
path: './asn-prefixes.lmdb',
|
||||
encoding: 'msgpack',
|
||||
readOnly: true,
|
||||
});
|
||||
|
||||
const cloudflare = db.get(13335);
|
||||
console.log(cloudflare.ipv4); // ["1.0.0.0/24", ...]
|
||||
|
||||
db.close();
|
||||
```
|
||||
|
||||
## Update frequency
|
||||
|
||||
The index is rebuilt **daily at 04:00 UTC** via a scheduled GitHub Actions workflow. Each run downloads the latest snapshot from ipverse/as-ip-blocks and overwrites the `latest` release assets.
|
||||
|
||||
## Data source
|
||||
|
||||
IP prefix data is sourced from [ipverse/as-ip-blocks](https://github.com/ipverse/as-ip-blocks), which aggregates routing data from public BGP sources.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
292
package-lock.json
generated
Normal file
292
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
{
|
||||
"name": "asn-index",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "asn-index",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"lmdb": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@harperfast/extended-iterable": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz",
|
||||
"integrity": "sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-darwin-arm64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.2.tgz",
|
||||
"integrity": "sha512-ZTEuSwB3QHOA6Jal4bi0oxAV1MK3xtzS3oUMq5OK3HSXNN4A79f9dhieZA0hgvwOTaGzEWmTd8Fg9XSkBIhAWw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-darwin-x64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.2.tgz",
|
||||
"integrity": "sha512-Zs+mdB6gNqpPK6ybNbqFoSU+DCIdhE8tqeaAzRs+hNJt8V43PRvTVxu1UPBHwK2917FnQ4dL5/OIoqHDa+9Dpw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-linux-arm": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.2.tgz",
|
||||
"integrity": "sha512-GhdC4huGWDzcbZWfS+G3dW4/TopNUnO+/E7aVdfWIhslSs1FI2+sVo94040S9BPJ7lNpnf1zVxaBlLmqZpKhcw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-linux-arm64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.2.tgz",
|
||||
"integrity": "sha512-hT6JPw5hDCXzppBgpIFS/cQp4v2LqNMgd5nuo4U9H5/wnbMS7Prh0twu5IbDvzYZf2a/xPTXtTDRuUiFc39lEw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-linux-x64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.2.tgz",
|
||||
"integrity": "sha512-aTBBxTQGdgKcqZD6ywIVCIbCIJ3fJ28OhzCxgl3zGQzzJwkDt5TSIuBtMt4oKZMgDSjuRBjtID9TOUvSRg8IQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-win32-arm64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.2.tgz",
|
||||
"integrity": "sha512-mqfNN5zb3z3QnHEPaV4Zv5zd3BhlcL+uqPNF7kGRkmCaRHuh6T9N5g/4ZqOiNHNPWglv3g8Ut15XxCKZjf6jHw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@lmdb/lmdb-win32-x64": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.2.tgz",
|
||||
"integrity": "sha512-JhPxlA8sIxPIdS78e4LeNfTlkF+2I/r98jKXf90pf+yhMCzyLkphcvbnWv7YL8yckp32c1uKZ1vf/JqcSiplHg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
|
||||
"integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
|
||||
"integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
|
||||
"integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lmdb": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.2.tgz",
|
||||
"integrity": "sha512-od5AWh1MNylIOeX7MB7TV627MM9tzyOUn8U8FZOeWKpWFnMU5FS9pu5t41pS4+pi7OxHRyk5QVRhuUimHjfkmg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@harperfast/extended-iterable": "^1.0.3",
|
||||
"msgpackr": "^1.11.2",
|
||||
"node-addon-api": "^6.1.0",
|
||||
"node-gyp-build-optional-packages": "5.2.2",
|
||||
"ordered-binary": "^1.5.3",
|
||||
"weak-lru-cache": "^1.2.2"
|
||||
},
|
||||
"bin": {
|
||||
"download-lmdb-prebuilds": "bin/download-prebuilds.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@lmdb/lmdb-darwin-arm64": "3.5.2",
|
||||
"@lmdb/lmdb-darwin-x64": "3.5.2",
|
||||
"@lmdb/lmdb-linux-arm": "3.5.2",
|
||||
"@lmdb/lmdb-linux-arm64": "3.5.2",
|
||||
"@lmdb/lmdb-linux-x64": "3.5.2",
|
||||
"@lmdb/lmdb-win32-arm64": "3.5.2",
|
||||
"@lmdb/lmdb-win32-x64": "3.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/msgpackr": {
|
||||
"version": "1.11.9",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.9.tgz",
|
||||
"integrity": "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw==",
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"msgpackr-extract": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/msgpackr-extract": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
|
||||
"integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build-optional-packages": "5.2.2"
|
||||
},
|
||||
"bin": {
|
||||
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-gyp-build-optional-packages": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
||||
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"node-gyp-build-optional-packages": "bin.js",
|
||||
"node-gyp-build-optional-packages-optional": "optional.js",
|
||||
"node-gyp-build-optional-packages-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ordered-binary": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz",
|
||||
"integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/weak-lru-cache": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
|
||||
"integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
18
package.json
Normal file
18
package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "asn-index",
|
||||
"version": "1.0.0",
|
||||
"description": "Daily-updated ASN prefix index in JSON and LMDB formats",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:json": "bash scripts/build.sh",
|
||||
"build:lmdb": "node scripts/build-lmdb.mjs",
|
||||
"build": "npm run build:json && npm run build:lmdb"
|
||||
},
|
||||
"dependencies": {
|
||||
"lmdb": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
}
|
||||
}
|
||||
44
scripts/build-lmdb.mjs
Normal file
44
scripts/build-lmdb.mjs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env node
|
||||
import { open } from 'lmdb';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const DIST_DIR = resolve('./dist');
|
||||
const JSON_PATH = resolve(DIST_DIR, 'asn-prefixes.json');
|
||||
const LMDB_PATH = resolve(DIST_DIR, 'asn-prefixes.lmdb');
|
||||
const TAR_PATH = resolve(DIST_DIR, 'asn-prefixes-lmdb.tar.gz');
|
||||
|
||||
console.log('=== LMDB Builder ===');
|
||||
console.log(`Reading ${JSON_PATH}...`);
|
||||
|
||||
const data = JSON.parse(readFileSync(JSON_PATH, 'utf8'));
|
||||
const entries = Object.entries(data);
|
||||
console.log(`Loaded ${entries.length} ASN entries.`);
|
||||
|
||||
console.log(`Opening LMDB at ${LMDB_PATH}...`);
|
||||
const db = open({
|
||||
path: LMDB_PATH,
|
||||
mapSize: 256 * 1024 * 1024, // 256 MB
|
||||
encoding: 'msgpack',
|
||||
});
|
||||
|
||||
console.log('Writing entries...');
|
||||
await db.transaction(() => {
|
||||
for (const [asn, prefixes] of entries) {
|
||||
db.put(Number(asn), prefixes);
|
||||
}
|
||||
});
|
||||
|
||||
const count = db.getCount();
|
||||
console.log(`Written ${count} entries to LMDB.`);
|
||||
|
||||
await db.close();
|
||||
console.log('LMDB closed.');
|
||||
|
||||
console.log(`Creating tarball: ${TAR_PATH}...`);
|
||||
execSync(
|
||||
`tar -czf "${TAR_PATH}" -C "${DIST_DIR}" asn-prefixes.lmdb`,
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
console.log('Done.');
|
||||
59
scripts/build.sh
Normal file
59
scripts/build.sh
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SOURCE_URL="https://github.com/ipverse/as-ip-blocks/releases/latest/download/as-ip-blocks.tar.gz"
|
||||
DIST_DIR="./dist"
|
||||
OUTPUT_FILE="${DIST_DIR}/asn-prefixes.json"
|
||||
TMPDIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
echo "Cleaning up temp directory: ${TMPDIR}"
|
||||
rm -rf "${TMPDIR}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "=== ASN Index Builder ==="
|
||||
echo "Source: ${SOURCE_URL}"
|
||||
|
||||
mkdir -p "${DIST_DIR}"
|
||||
|
||||
echo "Downloading as-ip-blocks archive..."
|
||||
curl -fsSL --retry 3 --retry-delay 5 -o "${TMPDIR}/as-ip-blocks.tar.gz" "${SOURCE_URL}"
|
||||
echo "Download complete."
|
||||
|
||||
echo "Extracting archive..."
|
||||
tar -xzf "${TMPDIR}/as-ip-blocks.tar.gz" -C "${TMPDIR}"
|
||||
echo "Extraction complete."
|
||||
|
||||
AS_DIR="${TMPDIR}/as"
|
||||
if [[ ! -d "${AS_DIR}" ]]; then
|
||||
echo "ERROR: Expected 'as/' directory not found in archive." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building ${OUTPUT_FILE}..."
|
||||
|
||||
# Build the combined JSON object using jq
|
||||
# Each per-ASN file is named like <asn>.json and contains:
|
||||
# { "asn": 13335, "handle": "...", "description": "...", "subnets": { "ipv4": [...], "ipv6": [...] } }
|
||||
|
||||
find "${AS_DIR}" -name 'aggregated.json' -print0 \
|
||||
| sort -z \
|
||||
| xargs -0 jq -c '
|
||||
select(
|
||||
((.prefixes.ipv4 // []) | length) > 0
|
||||
or ((.prefixes.ipv6 // []) | length) > 0
|
||||
)
|
||||
| {
|
||||
key: (.asn | tostring),
|
||||
value: {
|
||||
ipv4: (.prefixes.ipv4 // []),
|
||||
ipv6: (.prefixes.ipv6 // [])
|
||||
}
|
||||
}
|
||||
' \
|
||||
| jq -s 'from_entries' \
|
||||
> "${OUTPUT_FILE}"
|
||||
|
||||
ASN_COUNT=$(jq 'keys | length' "${OUTPUT_FILE}")
|
||||
echo "Done. ${ASN_COUNT} ASNs written to ${OUTPUT_FILE}."
|
||||
88
scripts/verify.mjs
Normal file
88
scripts/verify.mjs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Local verification script — not used in CI, just for sanity-checking the build.
|
||||
* Usage: node scripts/verify.mjs
|
||||
*/
|
||||
import { open } from 'lmdb';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const DIST_DIR = resolve('./dist');
|
||||
|
||||
// ── 1. Verify JSON ──────────────────────────────────────────────────────────
|
||||
console.log('── JSON verification ──────────────────────────────────────────');
|
||||
const json = JSON.parse(readFileSync(resolve(DIST_DIR, 'asn-prefixes.json'), 'utf8'));
|
||||
const jsonKeys = Object.keys(json);
|
||||
console.log(`Total ASNs in JSON : ${jsonKeys.length}`);
|
||||
|
||||
const noData = jsonKeys.filter(k => json[k].ipv4.length === 0 && json[k].ipv6.length === 0);
|
||||
console.log(`ASNs with no prefixes (should be 0): ${noData.length}`);
|
||||
|
||||
// Spot-check well-known ASNs
|
||||
const SPOT = [
|
||||
{ asn: '13335', name: 'Cloudflare', expectedIpv4: '1.1.1.0/24' },
|
||||
{ asn: '15169', name: 'Google', expectedIpv4: '8.8.8.0/24' },
|
||||
{ asn: '16509', name: 'Amazon AWS', expectedIpv4: null },
|
||||
];
|
||||
for (const { asn, name, expectedIpv4 } of SPOT) {
|
||||
const entry = json[asn];
|
||||
if (!entry) {
|
||||
console.log(` [FAIL] ${name} (AS${asn}) — not found`);
|
||||
continue;
|
||||
}
|
||||
const ipv4ok = expectedIpv4 ? entry.ipv4.includes(expectedIpv4) : entry.ipv4.length > 0;
|
||||
const status = ipv4ok ? 'OK ' : 'WARN';
|
||||
console.log(` [${status}] ${name} (AS${asn}) — ${entry.ipv4.length} IPv4, ${entry.ipv6.length} IPv6 prefixes${expectedIpv4 ? ` (${expectedIpv4}: ${ipv4ok ? 'present' : 'MISSING'})` : ''}`);
|
||||
}
|
||||
|
||||
// ── 2. Verify LMDB ──────────────────────────────────────────────────────────
|
||||
console.log('\n── LMDB verification ──────────────────────────────────────────');
|
||||
const db = open({
|
||||
path: resolve(DIST_DIR, 'asn-prefixes.lmdb'),
|
||||
encoding: 'msgpack',
|
||||
readOnly: true,
|
||||
});
|
||||
|
||||
const dbCount = db.getCount();
|
||||
console.log(`Total entries in LMDB: ${dbCount}`);
|
||||
console.log(`Match JSON count: ${dbCount === jsonKeys.length ? 'YES' : `NO (delta: ${dbCount - jsonKeys.length})`}`);
|
||||
|
||||
for (const { asn, name, expectedIpv4 } of SPOT) {
|
||||
const entry = db.get(Number(asn));
|
||||
if (!entry) {
|
||||
console.log(` [FAIL] ${name} (AS${asn}) — not found`);
|
||||
continue;
|
||||
}
|
||||
const ipv4ok = expectedIpv4 ? entry.ipv4.includes(expectedIpv4) : entry.ipv4.length > 0;
|
||||
const status = ipv4ok ? 'OK ' : 'WARN';
|
||||
console.log(` [${status}] ${name} (AS${asn}) — ${entry.ipv4.length} IPv4, ${entry.ipv6.length} IPv6 prefixes`);
|
||||
}
|
||||
|
||||
// Verify a random sample from JSON matches LMDB
|
||||
console.log('\nRandom sample cross-check (10 entries):');
|
||||
const sampleKeys = jsonKeys.sort(() => Math.random() - 0.5).slice(0, 10);
|
||||
let mismatches = 0;
|
||||
for (const k of sampleKeys) {
|
||||
const fromJson = json[k];
|
||||
const fromLmdb = db.get(Number(k));
|
||||
if (!fromLmdb) { mismatches++; console.log(` [FAIL] AS${k} missing in LMDB`); continue; }
|
||||
const match =
|
||||
JSON.stringify(fromJson.ipv4.sort()) === JSON.stringify(fromLmdb.ipv4.sort()) &&
|
||||
JSON.stringify(fromJson.ipv6.sort()) === JSON.stringify(fromLmdb.ipv6.sort());
|
||||
if (!match) { mismatches++; console.log(` [FAIL] AS${k} data mismatch`); }
|
||||
else console.log(` [OK ] AS${k}`);
|
||||
}
|
||||
if (mismatches === 0) console.log('All samples match.');
|
||||
|
||||
await db.close();
|
||||
|
||||
// ── 3. File sizes ────────────────────────────────────────────────────────────
|
||||
console.log('\n── Artifact sizes ─────────────────────────────────────────────');
|
||||
import { statSync } from 'node:fs';
|
||||
const fmt = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
||||
for (const name of ['asn-prefixes.json', 'asn-prefixes-lmdb.tar.gz']) {
|
||||
const { size } = statSync(resolve(DIST_DIR, name));
|
||||
console.log(` ${name}: ${fmt(size)}`);
|
||||
}
|
||||
|
||||
console.log('\nVerification complete.');
|
||||
Loading…
Add table
Add a link
Reference in a new issue