README
¶
asname
asname is a fast, offline command-line utility written in Go for resolving IP addresses, hostnames and URLs to their Autonomous System Number (ASN), the AS owner's name, and the geographical country.
Lookups are answered from local LC-trie databases, so there is no per-query network call and no API key or rate limit to worry about.
Features
- Blazing Fast: Uses offline LC-trie databases for instantaneous IP lookups.
- Takes Whatever You Have: An IP address, a hostname, a URL you pasted from a browser, or a file listing any mix of them.
- Pretty Cards & JSONL: Format results as clean multi-line cards (
--pretty) or streamable JSON objects (--json). - Real-Time Streaming: Feed targets directly through standard input pipes (
--stream). - REST API Server: Run as an instant in-memory HTTP daemon (
--rest) on port8086. - Names the Actual Owner: An optional database built from the RIRs' bulk whois dumps resolves an address to the netblock it was assigned in, so a suballocation reports the customer holding it rather than the datacentre announcing it.
- Says What It Is: Another optional database classifies an address as cloud, CDN, hosting, residential ISP, mobile or Tor exit, from the providers' own published ranges and operator-declared network types.
- Auto-Updating: Automatically fetches the latest RouteViews RIB dumps, RIPE ASN names, and RIR Delegation Statistics to build and maintain its own fresh databases when they get older than 30 days.
- Fully Standalone: A single static binary — no Cgo, and no
geoiplookupor other system tool to install alongside it.
Installation
go install github.com/flyingllama87/asname/cmd/asname@latest
Or build and install from a clone using the provided Makefile, which stamps the version into the binary and links it statically:
make build
sudo make install
This compiles the tool and places the asname binary into /usr/local/bin/.
Run make test to run the test suite.
Usage
Simply pass an IP address to resolve its information:
$ asname 8.8.8.8
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States
The argument can equally be a hostname. Every address the name resolves to is looked up, one line each:
$ asname dns.google
Host: dns.google → IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States
Host: dns.google → IP: 8.8.4.4 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States
Or a URL, so you can paste one straight from a browser. The scheme, credentials, port, path, query and fragment are stripped and whatever host remains is looked up:
$ asname 'https://dns.google:443/resolve?name=example.com'
Host: dns.google → IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States
Or a file listing any mix of the above, one entry per line. Blank lines and # comments are ignored, and only the first field of a line is read, so columnar files work as they are:
$ cat hosts.txt
# resolvers to check
8.8.8.8
1.1.1.1 # cloudflare
https://github.com/anthropics
$ asname hosts.txt
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States
IP: 1.1.1.1 → ASN: AS13335 → Name: CLOUDFLARENET - Cloudflare, Inc., US → Country: AU, Australia
Host: github.com → IP: 4.237.22.38 → ASN: AS8075 → Name: MICROSOFT-CORP-MSN-AS-BLOCK - Microsoft Corporation, US → Country: US, United States
Entries that cannot be resolved are reported on stderr and the rest of the file is still printed; asname then exits non-zero. Names are resolved concurrently, so a long file is not paced by DNS latency.
An argument that parses as an IP address is always treated as one; otherwise an existing file is read as a list, and anything else is treated as a hostname or URL.
Add --reverse-dns (or -r) to also send a reverse DNS query and include PTR names in the output:
$ asname --reverse-dns 8.8.8.8
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States → Reverse DNS: dns.google
City lookups
City-level geolocation is optional and off by default, because the database is
large. Enable it once with --city (or -c), which downloads it:
$ asname --city 8.8.8.8
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States → City: Mountain View, California
After that the flag is not needed — the city is included whenever the database is present, and refreshed along with everything else:
$ asname 1.1.1.1
IP: 1.1.1.1 → ASN: AS13335 → Name: CLOUDFLARENET - Cloudflare, Inc., US → Country: AU, Australia → City: Sydney, New South Wales
Use --no-city to suppress it for one run, or delete ~/.asname/city.mmdb to
opt back out for good. asname update refreshes the city database only once you
already have it, so it is never fetched unasked.
Treat the city as a hint, not a fact. DB-IP rate the free Lite database at
an accuracy index of 77, against 96 for their commercial one. VPNs, mobile
carriers, CGNAT and anycast routinely place an address hundreds of kilometres
from where it really is — dns.google's IPv6 anycast address reports as
Montreal, for instance. The ASN and AS name come from BGP and are solid; the
city is a best guess.
Registry netblocks
The ASN tells you who announces an address, which is often not who is using it. A suballocation inside a datacentre's range is announced by the datacentre, so the AS name is theirs and the customer's name appears nowhere.
The RIRs record that assignment as an inetnum object in their whois databases,
and they publish those databases in bulk. asname can build a local index of
them, enabled once with --netblock (or -n):
$ asname --netblock 183.177.54.135
IP: 183.177.54.135 → ASN: AS15830 → Name: Equinix Equinix (EMEA) Acquisition Enterprises B.V., NL → Country: AU, Australia → Netblock: SISS-SY4 (Secure Internet Storage Solutions)
Like the city, the flag is only needed once: from then on the netblock is
included whenever the database is present. --no-netblock suppresses it for one
run, and deleting ~/.asname/netblock.db opts back out for good.
Ranges nest, and the most specific one wins — an eight-address assignment inside a /22 allocation reports the assignment, not the allocation.
Coverage is not uniform, because the registries do not all publish the same thing:
| Registry | Region | Status |
|---|---|---|
| APNIC | Asia-Pacific | Published openly |
| RIPE NCC | Europe, Middle East | Published openly |
| AFRINIC | Africa | Published openly |
| LACNIC | Latin America | Anonymised. The public dump carries only status, city and country — no owner — so it is not used at all |
| ARIN | North America | Requires a signed agreement. See below |
Both gaps can be filled a query at a time over whois instead — see Filling the gaps with whois.
ARIN does not publish its database openly. Access needs the Bulk Whois Terms of
Use signed and the request
approved, after which ARIN issue an API key. Their terms restrict redistribution,
so asname can only build this part of the database from a key you obtained
yourself:
export ASNAME_ARIN_APIKEY=...
asname update --netblock-only
Without it, addresses in the ARIN region simply have no netblock and fall back to what the ASN says — which is most of North America, so it is worth having if you look at those addresses often.
Filling the gaps with whois
Neither registry publishes a dump asname can index, but both will happily answer a question about a single address over port 43. So when an address has no offline netblock, asname can ask the registry directly:
$ asname 8.8.8.8
asname: 8.8.8.8 has no offline netblock: ARIN and LACNIC do not publish theirs
asname: in a form that can be indexed offline. Query whois over the network for
asname: addresses like it? Either answer is remembered for an hour. [y/N] y
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States → Netblock: GOGL (Google LLC) [whois]
Every other lookup asname does is offline, so this one is asked about rather
than assumed. Either answer is remembered for an hour, in
~/.asname/whois-consent.json, so a session's work is one question rather than
one per address. After the hour it asks again.
Answers that came from the network are marked [whois], because unlike the rest
of the output they are not reproducible offline and reveal to the registry which
addresses you are looking at.
--whoisqueries without asking, and does not record an answer — use this in scripts.--no-whoisnever queries and never asks.- With no terminal to ask (a pipe, a cron job), asname does not query and prints
a one-line reminder that
--whoisexists.
The query goes to whois.iana.org first to find which registry holds the
address, then to that registry, so it works for any address and not just the two
gaps. asname only reaches for it when the offline database has nothing and the
address is one some registry has actually delegated, which keeps it away from
reserved and unallocated space.
Registries rate-limit port 43 hard, so one run stops after 25 online lookups and says so. A throttled registry answers with a well-formed but empty document; that is reported as a failure rather than silently shown as an unknown netblock.
--whois also works with no netblock database at all, if you would rather ask
the registries every time than keep 300 MB on disk.
Each registry also publishes placeholder objects covering the ranges it does not hold, so that a whois query for someone else's address points you at the right registry instead of returning nothing. Those are dropped while building, since a placeholder naming APNIC is worse than no answer at all.
Building the database downloads roughly 290 MB of registry dumps, takes about two minutes and needs ~1.5 GB of memory while it runs. The result is around 300 MB on disk holding some 6.7 million ranges. It is read from disk by binary search rather than loaded into memory, so a lookup stays instant and costs a few kilobytes of RAM.
Network categories
Who owns an address and what it is for are different questions. --category
(or -C) answers the second: cloud, CDN, hosting, residential ISP, mobile,
university, Tor exit and so on.
$ asname -C 13.32.0.1
IP: 13.32.0.1 → ASN: AS16509 → Name: AMAZON-02 - Amazon.com, Inc., US → Country: US, United States → Category: cdn, cloud:aws
$ asname 139.130.4.5
IP: 139.130.4.5 → ASN: AS1221 → Name: ASN-TELSTRA Telstra Limited, AU → Country: AU, Australia → Category: isp, mobile
Like the other optional databases, the flag is needed once; after that its
presence on disk is enough. --no-category suppresses it for a run. The
database is small — under a megabyte — and builds in a few seconds.
The two halves of it are not equally trustworthy, and the difference matters.
Prefixes come from the providers themselves. AWS, Google, Oracle, Fastly,
Cloudflare, DigitalOcean, Linode and Vultr all publish the exact ranges they
use, and AWS goes further and names the service, which is how 13.32.0.1 is
reported as a CDN while 52.95.110.1 is just a machine. The Tor Project
publishes its exit list the same way. When one of these names an address, that
is the provider speaking about its own network and it settles the question.
ASNs come from bgp.tools' operator tags and PeeringDB's self-reported network
type. These describe the operator, not the address: the tag means the AS is
associated with that thing somewhere, not that every address in it is. So
asname -C 8.8.8.8 reports cdn, hosting, vpn — all true of Google, none of
them specifically true of that resolver. Read AS-level answers as "this is the
sort of network it is", not as a fact about the address.
Because of that, a prefix match wins outright and the AS tags are not consulted;
otherwise every EC2 address would inherit vpn from Amazon's AS, on the grounds
that somebody, somewhere, runs a VPN on EC2.
Three of bgp.tools' tags are left out entirely for the same reason. tor sits
on a quarter of the eyeball ISPs in their data, because subscribers run relays.
anycast sits on Telstra, Google and Amazon, because everyone large anycasts
something. biznet means the network sells business connectivity, which
describes an ISP rather than the business at the far end.
What is missing is VPN and proxy detection, beyond operators who run their own AS. Most consumer VPN exits are rented from ordinary cloud providers and are, from routing data alone, indistinguishable from any other virtual machine. Telling them apart takes active measurement — connecting to the service and watching where it comes out — which is why that data is sold rather than published. The Tor exit list is the one exact, free piece of it.
The bgp.tools contact address
bgp.tools asks that clients identify themselves rather than arrive with a default user agent, so the first build asks for an address:
asname: some of the categories (hosting, ISP, VPN, CDN) come from bgp.tools,
asname: which asks that clients identify themselves with a contact address so
asname: they can get in touch if a client misbehaves. It is sent to bgp.tools
asname: alone, in the User-Agent header, and to none of the other sources.
asname: Contact email (blank to skip bgp.tools):
It is stored in ~/.asname/contact.json and asked once. It is sent to
bgp.tools and nowhere else — every other source is fetched with the plain user
agent, since none of them asked, and handing a personal address to a dozen
unrelated hosts is not a fair trade for a tag. Leaving it blank is remembered
too, and simply builds without their tags.
Set ASNAME_CONTACT_EMAIL or pass --contact-email to skip the question, which
is what you want in a script; neither is written to disk. With no terminal to
ask on, the build says so and leaves bgp.tools out.
Their data carries no explicit licence and they ask for 24-hour caching on it,
which a monthly rebuild comfortably satisfies. If you are going to redistribute
anything built from it, talk to admin@bgp.tools first. PeeringDB rate-limits
anonymous API access, so rebuilding repeatedly in quick succession will get a
429; the build warns and carries on with the other sources, which leaves the
database thinner than it should be — check the per-source counts if you care.
Add --uniform (or -u) to print aligned fields:
$ asname -u -r 8.8.8.8
IP: 8.8.8.8 → ASN: AS15169 → Name: GOOGLE - Google LLC, US → Country: US, United States → Reverse DNS: dns.google
Pretty mode
Add --pretty (or -p) to display results in a structured, multi-line card layout with plenty of whitespace and terminal color highlights:
$ asname --pretty 8.8.8.8
Target: 8.8.8.8
────────────────────────────────────────────────────────────
Address Details:
IP Address: 8.8.8.8 (IPv4)
Autonomous System:
ASN: AS15169
Organization: GOOGLE - Google LLC, US
Location:
Country: United States (US)
City: Mountain View, California
Registry Netblock:
Netname / Org: GOGL (Google LLC)
Source: Live WHOIS
Network Classification:
Category: cdn, content, hosting, vpn
────────────────────────────────────────────────────────────
Colors are enabled automatically on interactive terminals and suppressed when piped, unless forced with --color. Use --no-color (or set the NO_COLOR environment variable) to disable colors explicitly.
JSON lines mode
Add --json (or -j) to emit results in JSON Lines (JSONL) format, one JSON object per resolved IP:
$ asname --json 8.8.8.8
{"target":"8.8.8.8","host":null,"ip":"8.8.8.8","version":4,"asn":{"number":15169,"asn_string":"AS15169","name":"GOOGLE - Google LLC, US","announced":true},"country":{"code":"US","name":"United States"},"city":{"name":"Mountain View, California","present":true},"category":{"tags":["cdn","content","hosting","vpn"],"raw":"cdn, content, hosting, vpn"}}
Streaming mode
Use --stream (or -s, or -) to read targets continuously from standard input in real time. Queries are resolved concurrently with a single retry on transient DNS failures and output is flushed immediately:
# Pipe network logs into asname with JSONL and filter with jq
tail -f /var/log/nginx/access.log | awk '{print $1}' | asname --stream --json | jq -c '{ip, asn: .asn.asn_string, country: .country.code}'
# Stream from network capture tools
tshark -T fields -e ip.src | asname --stream --json
REST API server
Run asname --rest to host a fast HTTP REST API daemon in the foreground on port 8086 (or configure via --listen / ASNAME_LISTEN):
$ asname --rest --city --netblock -r
asname REST API listening on http://127.0.0.1:8086
Databases: ASN (yes), Names (yes), Country (true), City (true), Netblock (true), Category (true)
Ready to handle requests. Press Ctrl+C to shut down.
The REST API supports querying URLs, hostnames, and IP addresses with zero manual sanitization:
# Health check
curl -s http://127.0.0.1:8086/health
# Query with URL / hostname / IP via query parameter
curl -s "http://127.0.0.1:8086/v1/lookup?q=https://dns.google/resolve&reverse_dns=true"
# Direct path lookup
curl -s "http://127.0.0.1:8086/v1/lookup/1.1.1.1"
# Base64URL-encoded target lookup
curl -s "http://127.0.0.1:8086/v1/lookup/b64/aHR0cHM6Ly9leGFtcGxlLmNvbQ"
# Bulk batch lookup (JSON or plain text)
curl -s -X POST http://127.0.0.1:8086/v1/bulk \
-H "Content-Type: application/json" \
-d '{"targets": ["8.8.8.8", "1.1.1.1", "dns.google"], "reverse_dns": true}'
Manual Updates
You can manually trigger an update of the local databases using:
asname update
You can also update specific databases using the --db-only, --names-only, --country-only, --city-only, --netblock-only, or --category-only flags.
Database Locations
By default, asname stores its auto-updating databases in your home directory under ~/.asname/. The following files will be created:
~/.asname/asname.db: The binary LC-trie database for IP to ASN resolution.~/.asname/asn_db.txt: The text file mapping ASNs to their respective names.~/.asname/country.db: The binary LC-trie database for IP to Country mapping.~/.asname/city.mmdb: The DB-IP Lite city database, in MaxMind DB format. Only present if you have enabled city lookups; ~125 MB.~/.asname/netblock.db: The IP to registry netblock index, built from the RIRs' bulk whois dumps. Only present if you have enabled netblock lookups.~/.asname/category.db: The IP and ASN to category index. Only present if you have enabled category lookups; under 1 MB.~/.asname/whois-consent.json: Whether you agreed to live whois lookups, and when you were asked. Delete it to be asked again; it expires after an hour anyway.~/.asname/contact.json: The contact address sent to bgp.tools, or a note that you declined. Delete it to be asked again.
You can override this directory by setting the ASNAME_DIR environment variable or using the --dir flag. You can also override the path to individual databases using the ASNAME_DB, ASNAME_NAMES, ASNAME_COUNTRY, ASNAME_CITY, ASNAME_NETBLOCK, and ASNAME_CATEGORY environment variables or their respective flags.
Credits
The LC-trie implementation and the binary database format under pkg/binarytrie
and pkg/database are derived from asnlookup
by @banviktor, used under the Apache License 2.0.
Those files have been modified
for use here — the database type was reworked into an interface, trie
optimization was parallelised, and the marshalling header was changed. Each file
carries a notice to that effect.
Data comes from RouteViews (BGP RIB dumps), RIPE NCC (ASN names), and the RIR delegation statistics files (IP to country).
Category data comes from the providers' own published ranges (AWS, Google Cloud, Oracle, Fastly, Cloudflare, DigitalOcean, Linode and Vultr), the Tor Project exit list, PeeringDB network types, and bgp.tools operator tags.
Netblock data comes from the RIRs' bulk whois dumps — APNIC, RIPE NCC and AFRINIC, plus ARIN if you supply your own API key. Each is downloaded on request and none is redistributed with this tool.
City data is the DB-IP Lite database — IP Geolocation by DB-IP, licensed under CC BY 4.0. It is downloaded on request and is not redistributed with this tool.
License
Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.