Compare commits

..

2 Commits

Author SHA1 Message Date
Ari b761f70042 refactor: extract helper functions into dedicated helpers.js module 2026-04-08 15:33:06 -04:00
Ari 5afb9f48f5 feat(openalias): add BTC, ETH, and LTC OpenAlias records
Extends OpenAlias configuration with Bitcoin, Ethereum, and Litecoin donation addresses alongside the existing Monero entry.

Also tightens the openalias() helper to use strict type checks.
2026-03-22 17:41:49 -04:00
2 changed files with 158 additions and 154 deletions
+6 -154
View File
@@ -1,6 +1,8 @@
// @ts-check
/// <reference path="types-dnscontrol.d.ts" />
require("./helpers.js");
var REG_101DOMAIN = NewRegistrar("none");
var DNS_CLOUDFLARE = NewDnsProvider("cloudflare");
@@ -56,10 +58,10 @@ D("arirex.me", REG_101DOMAIN,
TXT("_github-pages-challenge-arirexouium", "0b62c2fb7a8422145d5b5e6637257d", CF_COMMENT("GitHub Pages Verify")),
// OpenAlias
openalias("xmr", "89dQNyY3E9gJGYrEeRw4EFAdezWQg7BBbHJdBpLRwrjH52ngNfAYRcEhAHQotCswGxTeSoFi5nQ7Gf86kySmXzuQE9CXjUH", {
name: "AriRexouium",
checksum: true,
}),
openalias("xmr", "89dQNyY3E9gJGYrEeRw4EFAdezWQg7BBbHJdBpLRwrjH52ngNfAYRcEhAHQotCswGxTeSoFi5nQ7Gf86kySmXzuQE9CXjUH", { name: "AriRexouium", checksum: true, }),
openalias("btc", "3DQEYUDquSvh6DX54o2Gw1TXT9ZGWWoRTh", { name: "AriRexouium", checksum: true, }),
openalias("eth", "0x3489c73A2F3DE6236624d43a3767f4dB8181d0c4", { name: "AriRexouium", checksum: true, }),
openalias("ltc", "Lg6sV5PfRrBvpQoAzsyCcWMs26aq5VBssS", { name: "AriRexouium", checksum: true, }),
);
D("achl.fr", REG_101DOMAIN,
@@ -141,153 +143,3 @@ cnames("arirex.me", rexcloud, [
CNAME(i.toLowerCase() + ".servarr", rexbox, CF_COMMENT(i))
);
});
/* ****************************************************************************************************************** *\
Helper Functions
\* ****************************************************************************************************************** */
/* -------------------------------------------------------------------------- *\
Basic Builders
\* -------------------------------------------------------------------------- */
/**
* Create CNAME records from "comment@subdomain" strings
* @param {string} domain - Domain to extend
* @param {string} target - Server target
* @param {string[]} records - Array of "comment@subdomain" strings
*/
function cnames(domain, target, records) {
records.forEach(function(rec) {
var part = rec.split("@");
D_EXTEND(domain, CNAME(part[1], target, CF_COMMENT(part[0])));
});
}
/**
* Create a Minecraft server subdomain with CNAME and SRV records
* @param {string} comment - Human-readable server name
* @param {string} subdomain - Subdomain for the server
* @param {string} domain - Domain to extend
* @param {number} port - Port the server listens on
*/
function minecraft(comment, subdomain, domain, port) {
var fqdn = subdomain + "." + domain + ".";
D_EXTEND(domain,
CNAME(subdomain, rexbox, CF_COMMENT("Minecraft > " + comment)),
SRV("_minecraft._tcp." + subdomain, 0, 0, port, fqdn, CF_COMMENT("Minecraft > " + comment)),
);
}
/* -------------------------------------------------------------------------- *\
Email Builders
\* -------------------------------------------------------------------------- */
/**
* Generate ProtonMail DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - ProtonMail verification token
* @param {string} dkimKey - ProtonMail DKIM domain key
* @returns {DomainModifier[]} Array of DNS records
*/
function protonmail(verification, dkimKey) {
return [
// Stage 1: Verify
TXT("@", "protonmail-verification=" + verification, CF_COMMENT("ProtonMail Verify")),
// Stage 2: MX
MX("@", 10, "mail.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
MX("@", 20, "mailsec.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:_spf.protonmail.ch mx ~all", CF_COMMENT("ProtonMail SPF")),
// Stage 3: DKIM
CNAME("protonmail._domainkey", "protonmail.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail2._domainkey", "protonmail2.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail3._domainkey", "protonmail3.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
// Stage 4: DMARC
dmarcRecord,
];
}
/**
* Generate SimpleLogin DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - SimpleLogin verification token
* @returns {DomainModifier[]} Array of DNS records
*/
function simplelogin(verification) {
return [
// Stage 1: Verify
TXT("@", "sl-verification=" + verification, CF_COMMENT("SimpleLogin Verify")),
// Stage 2: MX
MX("@", 10, "mx1.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
MX("@", 20, "mx2.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:simplelogin.co ~all", CF_COMMENT("SimpleLogin SPF")),
// Stage 4: DKIM
CNAME("dkim._domainkey", "dkim._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim02._domainkey", "dkim02._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim03._domainkey", "dkim03._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
// Stage 5: DMARC
dmarcRecord,
];
}
/* -------------------------------------------------------------------------- *\
Open Alias Builder
\* -------------------------------------------------------------------------- */
/**
* Generate OpenAlias TXT record
* @param {string} prefix - Application prefix (e.g., "xmr", "btc")
* @param {string} address - Recipient address
* @param {object} [opts] - Optional key-value pairs
* @param {string} [opts.name] - Recipient name
* @param {string} [opts.description] - Transaction description
* @param {string} [opts.amount] - Transaction amount
* @param {string} [opts.paymentId] - Payment ID (e.g., for Monero)
* @param {string} [opts.signature] - Address signature
* @param {boolean} [opts.checksum] - Optional CRC-32 checksum
* @returns {DomainModifier} TXT record
*/
function openalias(prefix, address, opts) {
// Prefix and address are minimum requirement.
// Everything else is optional.
opts = opts || {};
var record = "oa1:" + prefix + " recipient_address=" + address + ";";
if (opts.name) { record += " recipient_name=" + opts.name + ";"; }
if (opts.description) { record += " tx_description=" + opts.description + ";"; }
if (opts.amount) { record += " tx_amount=" + opts.amount + ";"; }
if (opts.paymentId) { record += " tx_payment_id=" + opts.paymentId + ";"; }
if (opts.signature) { record += " address_signature=" + opts.signature + ";"; }
if (opts.checksum) { record += " checksum=" + crc32(record.trim()).toString(16).toUpperCase() + ";"; }
// Checksum must be last: CRC-32 of the record trimmed of surrounding spaces.
return TXT("@", record, CF_COMMENT("OpenAlias > " + prefix.toUpperCase() + (opts.name ? " > " + opts.name : "")));
}
/**
* Calculate CRC-32 checksum of a string
* Handles UTF-8 strings correctly for use with DNSControl.
* @param {string} str - Input string
* @returns {number} CRC-32 value
* @see https://github.com/nabijaczleweli/openalias.rs/blob/master/src/crypto_addr.rs
*/
function crc32(str) {
// 1. Generate the CRC Table
var table = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
table[i] = c;
}
// 2. Convert string to UTF-8 "binary" string
// This ensures characters like '€' are treated as 3 bytes, not 1.
var utf8Str = unescape(encodeURIComponent(str));
// 3. Calculate CRC
var crc = 0xFFFFFFFF;
for (var k = 0; k < utf8Str.length; k++) {
crc = table[(crc ^ utf8Str.charCodeAt(k)) & 0xFF] ^ (crc >>> 8);
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}
+152
View File
@@ -0,0 +1,152 @@
/* ****************************************************************************************************************** *\
Helper Functions
\* ****************************************************************************************************************** */
/* -------------------------------------------------------------------------- *\
Basic Builders
\* -------------------------------------------------------------------------- */
/**
* Create CNAME records from "comment@subdomain" strings
* @param {string} domain - Domain to extend
* @param {string} target - Server target
* @param {string[]} records - Array of "comment@subdomain" strings
*/
function cnames(domain, target, records) {
records.forEach(function(rec) {
var part = rec.split("@");
D_EXTEND(domain, CNAME(part[1], target, CF_COMMENT(part[0])));
});
}
/**
* Create a Minecraft server subdomain with CNAME and SRV records
* @param {string} comment - Human-readable server name
* @param {string} subdomain - Subdomain for the server
* @param {string} domain - Domain to extend
* @param {number} port - Port the server listens on
*/
function minecraft(comment, subdomain, domain, port) {
var fqdn = subdomain + "." + domain + ".";
D_EXTEND(domain,
CNAME(subdomain, rexbox, CF_COMMENT("Minecraft > " + comment)),
SRV("_minecraft._tcp." + subdomain, 0, 0, port, fqdn, CF_COMMENT("Minecraft > " + comment)),
);
}
/* -------------------------------------------------------------------------- *\
Email Builders
\* -------------------------------------------------------------------------- */
/**
* Generate ProtonMail DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - ProtonMail verification token
* @param {string} dkimKey - ProtonMail DKIM domain key
* @returns {DomainModifier[]} Array of DNS records
*/
function protonmail(verification, dkimKey) {
return [
// Stage 1: Verify
TXT("@", "protonmail-verification=" + verification, CF_COMMENT("ProtonMail Verify")),
// Stage 2: MX
MX("@", 10, "mail.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
MX("@", 20, "mailsec.protonmail.ch.", CF_COMMENT("ProtonMail MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:_spf.protonmail.ch mx ~all", CF_COMMENT("ProtonMail SPF")),
// Stage 3: DKIM
CNAME("protonmail._domainkey", "protonmail.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail2._domainkey", "protonmail2.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
CNAME("protonmail3._domainkey", "protonmail3.domainkey." + dkimKey + ".domains.proton.ch.", CF_COMMENT("ProtonMail DKIM")),
// Stage 4: DMARC
dmarcRecord,
];
}
/**
* Generate SimpleLogin DNS records (MX, SPF, DMARC, verification, DKIM)
* @param {string} verification - SimpleLogin verification token
* @returns {DomainModifier[]} Array of DNS records
*/
function simplelogin(verification) {
return [
// Stage 1: Verify
TXT("@", "sl-verification=" + verification, CF_COMMENT("SimpleLogin Verify")),
// Stage 2: MX
MX("@", 10, "mx1.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
MX("@", 20, "mx2.simplelogin.co.", CF_COMMENT("SimpleLogin MX")),
// Stage 3: SPF
TXT("@", "v=spf1 include:simplelogin.co ~all", CF_COMMENT("SimpleLogin SPF")),
// Stage 4: DKIM
CNAME("dkim._domainkey", "dkim._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim02._domainkey", "dkim02._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
CNAME("dkim03._domainkey", "dkim03._domainkey.simplelogin.co.", CF_COMMENT("SimpleLogin DKIM")),
// Stage 5: DMARC
dmarcRecord,
];
}
/* -------------------------------------------------------------------------- *\
OpenAlias Builder
\* -------------------------------------------------------------------------- */
/**
* Generate OpenAlias TXT record
* @param {string} prefix - Application prefix (e.g., "xmr", "btc")
* @param {string} address - Recipient address
* @param {object} [opts] - Optional key-value pairs
* @param {string} [opts.name] - Recipient name
* @param {string} [opts.description] - Transaction description
* @param {string} [opts.amount] - Transaction amount
* @param {string} [opts.paymentId] - Payment ID (e.g., for Monero)
* @param {string} [opts.signature] - Address signature
* @param {boolean} [opts.checksum] - Optional CRC-32 checksum
* @returns {DomainModifier} TXT record
*/
function openalias(prefix, address, opts) {
// Prefix and address are minimum requirement.
// Everything else is optional.
opts = opts || {};
var record = "oa1:" + prefix + " recipient_address=" + address + ";";
if (typeof opts.name === "string") record += " recipient_name=" + opts.name + ";";
if (typeof opts.description === "string") record += " tx_description=" + opts.description + ";";
if (typeof opts.amount === "string") record += " tx_amount=" + opts.amount + ";";
if (typeof opts.paymentId === "string") record += " tx_payment_id=" + opts.paymentId + ";";
if (typeof opts.signature === "string") record += " address_signature=" + opts.signature + ";";
if (opts.checksum === true) record += " checksum=" + crc32(record.trim()).toString(16).toUpperCase() + ";";
// Checksum must be last: CRC-32 of the record trimmed of surrounding spaces.
return TXT("@", record, CF_COMMENT("OpenAlias > " + prefix.toUpperCase() + (opts.name ? " > " + opts.name : "")));
}
/**
* Calculate CRC-32 checksum of a string
* Handles UTF-8 strings correctly for use with DNSControl.
* @param {string} str - Input string
* @returns {number} CRC-32 value
* @see https://github.com/nabijaczleweli/openalias.rs/blob/master/src/crypto_addr.rs
*/
function crc32(str) {
// 1. Generate the CRC Table
var table = [];
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
table[i] = c;
}
// 2. Convert string to UTF-8 "binary" string
// This ensures characters like '€' are treated as 3 bytes, not 1.
var utf8Str = unescape(encodeURIComponent(str));
// 3. Calculate CRC
var crc = 0xFFFFFFFF;
for (var k = 0; k < utf8Str.length; k++) {
crc = table[(crc ^ utf8Str.charCodeAt(k)) & 0xFF] ^ (crc >>> 8);
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}