LGR Server provides a simple API to validate domain name labels against the Label Generation Rulesets (LGRs).
Using this API, a domain name registry or registrar can:
- determine whether a given label is valid against a particular LGR; and
- determine if what variants (if any) the label has, and their disposition (blocked or allocatable).
A live demo of the server is available at https://lgr.gavinbrown.xyz.
# curl -s http://localhost:8080/root-zone/und-Latn/xn--caf-dma | jq .
{
"u_label": "café",
"a_label": "xn--caf-dma",
"code_points": [
99,
97,
102,
233
],
"tag": "und-Latn",
"invalid_code_points": [],
"eligible": true,
"disposition": "valid",
"index_label": "xn--caf-dma",
"is_index_label": true,
"approx_variants": 4
}
A GET request of the form /{set}/{table}/{label} will return a JSON object with the structure above. {set} refers to the specific LGR set, while {table} must correspond to a language tag and maps to a file in the chosen LGR set. {label} is the domain name label (e.g. example) in either A-label or U-label format.
u_label- the "U-label" representation of the label in UTF-8a_label- the "A-label" representation of the label in Punycodecode_points- an array of Unicode code points represented as decimal integerstag- the language (or script) taginvalid_code_points- those code points in the label that are not valid for the LGReligible- a boolean indicating whether the label is allocatabledisposition- one ofvalid,invalid,blockedorallocatableindex_label- the "index" of the set of variant labels to which the given label belongsis_index_label- a boolean indicating whether the provided label is identical to the index labelapprox_variants- the estimated number of variant labels. Some labels can generate geometrically large numbers of variants, so this provides a hint on the likely size of the response to a/{table}/{label}/variantsrequest (see below)
$ curl -s http://localhost:8080/root-zone/und-Latn/xn--caf-dma/variants | jq .
[
{
"u_label": "caĎ",
"a_label": "xn--ca-cja98d",
"code_points": [
99,
97,
402,
233
],
"disposition": "blocked"
},
{
"u_label": "cáfé",
"a_label": "xn--cf-mia7a",
"code_points": [
99,
225,
102,
233
],
"disposition": "blocked"
},
{
"u_label": "cáƒé",
"a_label": "xn--c-ufay98d",
"code_points": [
99,
225,
402,
233
],
"disposition": "blocked"
}
]
A GET request of the form /{set}/{table}/{label}/variants will return a JSON array of objects, each of which represents a variant of {label}. Each object has u_label, a_label, code_points and disposition properties, whose semantics are the same as above.
A GET request of the form /{set}/{table}/{label}/{variant} will return a boolean JSON value (ie true or false) indicating whether {variant} is a variant of {label}.
LGR Server comes bundled with three sets of LGRs:
Use docker compose up. This will expose the server on port 8080.
Also:
$ docker run -p 8080:8080 gbxyz/lgr_server:latest
LGR Server is written in Python, so it can make use of the ICANN lgr-core library. This is my first serious piece of Python programming so please be gentle :)
The time taken to answer GET /{set}/{table}/{label} requests depends on the LGR set, on my development machine, the typical RTTs are:
root-zone: 100mssecond-level-reference: 70msfull-variant-set: 200ms
The time taken to answer GET /{set}/{table}/{label}/variants increases geometrically with the length of {label}, so the time required to compute the variants of foo is about 4 times longer than the time required for fo, and for foobar it's about 30 times longer. So caution should be exercised when making these requests in-band, especially using user-provided (or attacker-provided) values for {label}.
See LICENSE.