Web

Domains

Domain names read by the Public Suffix List: registrable domain, subdomain and suffix, same-site checks for redirects and cookies, and international names to punycode and back.

Version 1.0.0 · 5 functions · 2 crates

#urls#dns#cookies#security#i18n

Functions

What use domains.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use domain_parse, domain_registrable from domains (Quillo 0.6).

domain_parse(name: str) → anyor an error

The parts of a host, or of a URL's host: { host, unicode, registrable, subdomain, suffix, suffix_known, private_suffix }. "a.b.example.co.uk" has registrable "example.co.uk", subdomain "a.b" and suffix "co.uk". Suffixes run by companies count too ("github.io", "herokuapp.com": private_suffix true), so "me.github.io" is a site of its own. registrable is null for a bare suffix ("co.uk") or an IP address.

domain_registrable(name: str) → str?or an error

The registrable domain ("eTLD+1") of a host or URL, or null: what cookies, rate limits and "same site" checks should group by.

domain_same_site(a: str, b: str) → boolor an error

Whether two hosts or URLs are the same site, sharing a registrable domain: "app.example.com" and "www.example.com" are; "a.github.io" and "b.github.io" are not.

domain_to_ascii(name: str) → stror an error

An international domain name in ASCII (punycode) for DNS, certificates and links: "bücher.example" is "xn--bcher-kva.example". Lower-cases and checks it; an error when it cannot be a name.

domain_to_unicode(name: str) → str

A punycode name back in its own letters, for showing it: "xn--mnchen-3ya.de" is "münchen.de". Labels that do not decode stay as they were.

Get it

$ quillo install domains               # domains.qwer into this folder$ quillo install domains --example     # and its example, domains.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/domains.qwer$ curl -O https://quillolang.org/plugins/files/domains.qwe

Then, in your program, all of it or only what you call:

main.qwe
use domains.qweruse domain_parse, domain_registrable from domains      // Quillo 0.6: only these

It uses these crates, which cargo fetches the first time the plugin is built:

[dependencies]
psl = "2"idna = "1"

Example

domains.qwe
// Sign-ups grouped by the company's domain, a "same site" check for redirects, and international names shown in their own letters.////   quillo run domains.qwe      then open http://localhost:8080/domain?name=https://shop.example.co.uk/cartuse "domains.qwer"route GET /domain ? { name: str } = domain_parse(name)?route GET /signups {    let emails = ["ana@mail.acme.co.uk", "bo@acme.co.uk", "cy@sales.acme.co.uk", "di@other.com"]    group_by(emails, (e) => domain_registrable(split(e, "@")[1])? ?? "?") | keys}route GET /redirect ? { to: str } {    if !domain_same_site(to, "https://www.example.com")? { reply 400 { error: "redirects stay on example.com" } }    { ok: to }}route GET /idn = { ascii: domain_to_ascii("München.de")?, unicode: domain_to_unicode("xn--mnchen-3ya.de"), pages: domain_same_site("a.github.io", "b.github.io")? }
$ quillo run domains.qwe$ curl 'localhost:8080/domain?name=https://shop.example.co.uk/cart'$ curl 'localhost:8080/idn'$ curl 'localhost:8080/signups'

Source

domains.qwer
//! Domain names read by the Public Suffix List: the registrable domain of "shop.example.co.uk" is "example.co.uk", its subdomain "shop"; and international names to punycode ("münchen.de" is "xn--mnchen-3ya.de") and back.//! version: 1.0.0//!//! [dependencies]//! psl = "2"//! idna = "1"use serde_json::{json, Value};/// The host of a URL or a host name: no scheme, user, port, path or trailing dot, lower case, in ASCII (punycode).fn host(input: &str) -> Result<String, String> {    let mut s = input.trim();    if s.len() > 2000 {        return Err("names up to 2,000 characters".into());    }    if let Some(i) = s.find("://") {        s = &s[i + 3..];    }    s = s.split(['/', '?', '#']).next().unwrap_or("");    if let Some(i) = s.rfind('@') {        s = &s[i + 1..];    }    if !s.starts_with('[') {        s = s.split(':').next().unwrap_or("");    }    let s = s.trim_end_matches('.');    if s.is_empty() {        return Err(format!("no host in {:?}", input));    }    idna::domain_to_ascii(s).map_err(|_| format!("{:?} is not a valid domain name", s))}fn parts(h: &str) -> Value {    let suffix = psl::suffix(h.as_bytes());    let domain = psl::domain(h.as_bytes());    let suffix_s = suffix.map(|x| String::from_utf8_lossy(x.as_bytes()).into_owned());    let domain_s = domain.map(|x| String::from_utf8_lossy(x.as_bytes()).into_owned());    let sub = domain_s.as_ref().and_then(|d| h.strip_suffix(d.as_str())).map(|s| s.trim_end_matches('.').to_string()).filter(|s| !s.is_empty());    json!({        "host": h,        "unicode": idna::domain_to_unicode(h).0,        "registrable": domain_s,        "subdomain": sub,        "suffix": suffix_s,        "suffix_known": suffix.map_or(false, |x| x.is_known()),        "private_suffix": suffix.and_then(|x| x.typ()) == Some(psl::Type::Private),    })}/// The parts of a host, or of a URL's host: `{ host, unicode, registrable, subdomain, suffix, suffix_known, private_suffix }`. "a.b.example.co.uk" has registrable "example.co.uk", subdomain "a.b" and suffix "co.uk". Suffixes run by companies count too ("github.io", "herokuapp.com": `private_suffix` true), so "me.github.io" is a site of its own. `registrable` is null for a bare suffix ("co.uk") or an IP address.pub fn domain_parse(name: &str) -> Result<Value, String> {    let h = host(name)?;    if h.parse::<std::net::IpAddr>().is_ok() || h.starts_with('[') {        return Ok(json!({ "host": h, "unicode": h, "registrable": null, "subdomain": null, "suffix": null, "suffix_known": false, "private_suffix": false }));    }    Ok(parts(&h))}/// The registrable domain ("eTLD+1") of a host or URL, or null: what cookies, rate limits and "same site" checks should group by.pub fn domain_registrable(name: &str) -> Result<Option<String>, String> {    Ok(domain_parse(name)?["registrable"].as_str().map(String::from))}/// Whether two hosts or URLs are the same site, sharing a registrable domain: "app.example.com" and "www.example.com" are; "a.github.io" and "b.github.io" are not.pub fn domain_same_site(a: &str, b: &str) -> Result<bool, String> {    let (x, y) = (domain_registrable(a)?, domain_registrable(b)?);    Ok(x.is_some() && x == y)}/// An international domain name in ASCII (punycode) for DNS, certificates and links: "bücher.example" is "xn--bcher-kva.example". Lower-cases and checks it; an error when it cannot be a name.pub fn domain_to_ascii(name: &str) -> Result<String, String> {    host(name)}/// A punycode name back in its own letters, for showing it: "xn--mnchen-3ya.de" is "münchen.de". Labels that do not decode stay as they were.pub fn domain_to_unicode(name: &str) -> String {    idna::domain_to_unicode(name.trim()).0}
DKIM, SPF and DMARC email_auth.qwer · 1.0.0Messages signed and checked with DKIM (RSA and Ed25519), SPF records read and evaluated for a sending IP, and DMARC policies applied, with DNS records given as a map so it runs offline.emailsigningdnssecurity6 views · 5 downloadsSecurity headers security_headers.qwer · 1.0.0A response's security headers graded A+ to F: HSTS, CSP, framing, sniffing, referrers, permissions, cookies' flags, CORS and version leaks, each with its fix; and a good set to send.headerssecurityhttpcookies8 views · 6 downloadsCUID2 cuid2.qwer · 1.0.0CUID2 ids: secure, collision-resistant, starting with a letter, of the length you choose, that give away neither the time nor a count; and a check that a string is one.idsurlsdatabasesecurity7 views · 7 downloadsNano ID nanoid.qwer · 1.0.0Short random ids for links, invites and coupon codes: Nano ID with secure random, your own alphabet and length, readable codes for print, and the odds of a collision.idsurlssecuritycoupons7 views · 7 downloadsAccept-Language accept_language.qwer · 1.0.0The visitor's language from the Accept-Language header: read with its weights, and the best of your site's languages picked, en-GB matching en.i18nlanguagesheaderslocalization8 views · 8 downloadsCookies cookies.qwer · 1.0.0Set-Cookie headers built and read with every attribute, Cookie headers read and written, values signed or encrypted so the browser can keep but not change or read them, and a cookie jar for calling other sites.cookieshttpsigningencryption7 views · 8 downloads
Thread
77Useful? Like it, or add to its thread below.

Thread 0 posts

On Domains: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.