dnscontrol/pkg/js/helpers.js
imlonghao 04f34cf2e3
PORKBUN: support URL Forward (#3064)
Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
2024-08-26 14:50:25 -04:00

1988 lines
58 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
// How to keep this file clean:
// 1. Add new functions in alphabetical order when it makes sense.
// 2. Run [prettier](https://github.com/prettier/prettier) on the file to ensure
// your code conforms to our coding standard:
// npm install prettier
// node_modules/.bin/prettier --write pkg/js/helpers.js
// Development tip:
// This file is embeded in the binary via "go build". If you are
// debugging/developing this code, it may be faster to specify the
// -dev file to have helpers.js read from the file instead.
var conf = {
registrars: [],
dns_providers: [],
domains: [],
domain_names: [],
};
var defaultArgs = [];
function initialize() {
conf = {
registrars: [],
dns_providers: [],
domains: [],
};
defaultArgs = [];
}
function _isDomain(d) {
return (
_.isArray(d.nameservers) && _.isArray(d.records) && _.isString(d.name)
);
}
// Returns an array of domains which were registered so far during runtime
// Own function for compatibility reasons or if some day special processing would be required.
function getConfiguredDomains() {
return conf.domain_names;
}
// NewRegistrar returns an registrar object.
// For backwards compatibility, it accepts (name), (name, meta),
// (name, type), (name, type, meta).
function NewRegistrar() {
// For backwards compatibility, this is a wrapper around the legacy
// version of this function.
switch (arguments.length) {
case 1:
return oldNewRegistrar(arguments[0], '-');
case 2:
// x = NewRegistrar("myThing", "THING")
// x = NewRegistrar("myThing", { metakey: metavalue } )
if (typeof arguments[1] === 'object') {
return oldNewRegistrar(arguments[0], '-', arguments[1]);
}
break;
default: // do nothing
}
return oldNewRegistrar.apply(null, arguments);
}
function oldNewRegistrar(name, type, meta) {
if (type) {
type == 'MANUAL';
}
var reg = { name: name, type: type, meta: meta };
conf.registrars.push(reg);
return name;
}
function NewDnsProvider(name, type, meta) {
// For backwards compatibility, this is a wrapper around the legacy
// version of this function.
switch (arguments.length) {
case 1:
return oldNewDnsProvider(arguments[0], '-');
case 2:
// x = NewDnsProvider("myThing", "THING")
// x = NewDnsProvider("myThing", { metakey: metavalue } )
if (typeof arguments[1] === 'object') {
return oldNewDnsProvider(arguments[0], '-', arguments[1]);
}
break;
default: // do nothing
}
return oldNewDnsProvider.apply(null, arguments);
}
function oldNewDnsProvider(name, type, meta) {
if (typeof meta === 'object' && 'ip_conversions' in meta) {
meta.ip_conversions = format_tt(meta.ip_conversions);
}
var dsp = { name: name, type: type, meta: meta };
conf.dns_providers.push(dsp);
return name;
}
function newDomain(name, registrar) {
return {
name: name,
subdomain: '',
registrar: registrar,
meta: {},
records: [],
rawrecords: [],
recordsabsent: [],
dnsProviders: {},
defaultTTL: 0,
nameservers: [],
ignored_names: [],
ignored_targets: [],
unmanaged: [],
};
}
function processDargs(m, domain) {
// for each modifier, if it is a...
// function: call it with domain
// array: process recursively
// object: merge it into metadata
if (_.isFunction(m)) {
m(domain);
} else if (_.isArray(m)) {
for (var j in m) {
processDargs(m[j], domain);
}
} else if (_.isObject(m)) {
_.extend(domain.meta, m);
} else {
throw (
'WARNING: domain modifier type unsupported: ' +
typeof m +
' Domain: ' +
domain.name
);
}
}
// D(name,registrar): Create a DNS Domain. Use the parameters as records and mods.
function D(name, registrar) {
var domain = newDomain(name, registrar);
for (var i = 0; i < defaultArgs.length; i++) {
processDargs(defaultArgs[i], domain);
}
for (var i = 2; i < arguments.length; i++) {
var m = arguments[i];
processDargs(m, domain);
}
if (conf.domain_names.indexOf(name) !== -1) {
throw name + ' is declared more than once';
}
conf.domains.push(domain);
conf.domain_names.push(name);
}
function INCLUDE(name) {
var domain = _getDomainObject(name);
if (domain == null) {
throw (
name +
' was not declared yet and therefore cannot be updated. Use D() before.'
);
}
return function (d) {
d.records.push.apply(d.records, domain.obj.records);
};
}
// D_EXTEND(name): Update a DNS Domain already added with D(), or subdomain thereof
function D_EXTEND(name) {
var domain = _getDomainObject(name);
if (domain == null) {
throw (
name +
' was not declared yet and therefore cannot be updated. Use D() before.'
);
}
domain.obj.subdomain = name.substr(
0,
name.length - domain.obj.name.length - 1
);
for (var i = 1; i < arguments.length; i++) {
var m = arguments[i];
processDargs(m, domain.obj);
}
conf.domains[domain.id] = domain.obj; // let's overwrite the object.
}
// _getDomainObject(name): This implements the domain matching
// algorithm used by D_EXTEND(). Candidate matches are an exact match
// of the domain's name, or if name is a proper subdomain of the
// domain's name. The longest match is returned.
function _getDomainObject(name) {
var domain = null;
var domainLen = 0;
for (var i = 0; i < conf.domains.length; i++) {
var thisName = conf.domains[i]['name'];
var desiredSuffix = '.' + thisName;
var foundSuffix = name.substr(-desiredSuffix.length);
// If this is an exact match or the suffix matches...
if (name === thisName || foundSuffix === desiredSuffix) {
// If this match is a longer match than our current best match...
if (thisName.length > domainLen) {
domainLen = thisName.length;
domain = { id: i, obj: conf.domains[i] };
}
}
}
return domain;
}
// DEFAULTS provides a set of default arguments to apply to all future domains.
// Each call to DEFAULTS will clear any previous values set.
function DEFAULTS() {
defaultArgs = [];
for (var i = 0; i < arguments.length; i++) {
defaultArgs.push(arguments[i]);
}
}
// TTL(v): Set the TTL for a DNS record.
function TTL(v) {
if (_.isString(v)) {
v = stringToDuration(v);
}
return function (r) {
r.ttl = v;
};
}
function stringToDuration(v) {
var matches = v.match(/^(\d+)([smhdwny]?)$/);
if (matches == null) {
throw v + ' is not a valid duration string';
}
unit = 's';
if (matches[2]) {
unit = matches[2];
}
v = parseInt(matches[1]);
var u = { s: 1, m: 60, h: 3600 };
u['d'] = u.h * 24;
u['w'] = u.d * 7;
u['n'] = u.d * 30;
u['y'] = u.d * 365;
v *= u[unit];
return v;
}
// DefaultTTL(v): Set the default TTL for the domain.
function DefaultTTL(v) {
if (_.isString(v)) {
v = stringToDuration(v);
}
return function (d) {
d.defaultTTL = v;
};
}
function makeCAAFlag(value) {
return function (record) {
record.caaflag |= value;
};
}
// CAA_CRITICAL: Critical CAA flag
var CAA_CRITICAL = makeCAAFlag(1 << 7);
// DnsProvider("providerName", 0)
// nsCount of 0 means don't use or register any nameservers.
// nsCount not provider means use all.
function DnsProvider(name, nsCount) {
if (typeof nsCount === 'undefined') {
nsCount = -1;
}
return function (d) {
d.dnsProviders[name] = nsCount;
};
}
// A(name,ip, recordModifiers...)
var A = recordBuilder('A');
// AAAA(name,ip, recordModifiers...)
var AAAA = recordBuilder('AAAA');
// AKAMAICDN(name, target, recordModifiers...)
var AKAMAICDN = recordBuilder('AKAMAICDN');
// ALIAS(name,target, recordModifiers...)
var ALIAS = recordBuilder('ALIAS');
// AZURE_ALIAS(name, type, target, recordModifiers...)
var AZURE_ALIAS = recordBuilder('AZURE_ALIAS', {
args: [
['name', _.isString],
['type', validateAzureAliasType],
['target', _.isString],
],
transform: function (record, args, modifier) {
record.name = args.name;
record.target = args.target;
if (_.isObject(record.azure_alias)) {
record.azure_alias['type'] = args.type;
} else {
record.azure_alias = { type: args.type };
}
},
});
function validateAzureAliasType(value) {
if (!_.isString(value)) {
return false;
}
return ['A', 'AAAA', 'CNAME'].indexOf(value) !== -1;
}
// R53_ALIAS(name, target, type, recordModifiers...)
var R53_ALIAS = recordBuilder('R53_ALIAS', {
args: [
['name', _.isString],
['type', validateR53AliasType],
['target', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.target = args.target;
if (_.isObject(record.r53_alias)) {
record.r53_alias['type'] = args.type;
if (!_.isString(record.r53_alias['evaluate_target_health'])) {
record.r53_alias['evaluate_target_health'] = 'false';
}
} else {
record.r53_alias = {
type: args.type,
evaluate_target_health: 'false',
};
}
},
});
// R53_ZONE(zone_id)
function R53_ZONE(zone_id) {
return function (r) {
if (_isDomain(r)) {
r.meta.zone_id = zone_id;
} else if (_.isObject(r.r53_alias)) {
r.r53_alias['zone_id'] = zone_id;
} else {
r.r53_alias = { zone_id: zone_id };
}
};
}
// R53_EVALUATE_TARGET_HEALTH(enabled)
function R53_EVALUATE_TARGET_HEALTH(enabled) {
return function (r) {
if (_.isObject(r.r53_alias)) {
r.r53_alias['evaluate_target_health'] = enabled.toString();
} else {
r.r53_alias = { evaluate_target_health: enabled.toString() };
}
};
}
function validateR53AliasType(value) {
if (!_.isString(value)) {
return false;
}
return (
[
'A',
'AAAA',
'CNAME',
'CAA',
'MX',
'TXT',
'PTR',
'SPF',
'SRV',
'NAPTR',
].indexOf(value) !== -1
);
}
// CAA(name,tag,value, recordModifiers...)
var CAA = recordBuilder('CAA', {
// TODO(tlim): It should be an error if value is not 0 or 128.
args: [
['name', _.isString],
['tag', _.isString],
['value', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.caatag = args.tag;
record.target = args.value;
},
modifierNumber: function (record, value) {
record.caaflags |= value;
},
});
// CNAME(name,target, recordModifiers...)
var CNAME = recordBuilder('CNAME');
// DS(name, keytag, algorithm, digestype, digest)
var DS = recordBuilder('DS', {
args: [
['name', _.isString],
['keytag', _.isNumber],
['algorithm', _.isNumber],
['digesttype', _.isNumber],
['digest', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.dskeytag = args.keytag;
record.dsalgorithm = args.algorithm;
record.dsdigesttype = args.digesttype;
record.dsdigest = args.digest;
record.target = args.target;
},
});
// DHCID(name,target, recordModifiers...)
var DHCID = recordBuilder('DHCID');
// DNAME(name,target, recordModifiers...)
var DNAME = recordBuilder('DNAME');
// DNSKEY(name, flags, protocol, algorithm, publickey)
var DNSKEY = recordBuilder('DNSKEY', {
args: [
['name', _.isString],
['flags', _.isNumber],
['protocol', _.isNumber],
['algorithm', _.isNumber],
['publickey', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.dnskeyflags = args.flags;
record.dnskeyprotocol = args.protocol;
record.dnskeyalgorithm = args.algorithm;
record.dnskeypublickey = args.publickey;
record.target = args.target;
},
});
// name, priority, target, params
var HTTPS = recordBuilder('HTTPS', {
args: [
['name', _.isString],
['priority', _.isNumber],
['target', _.isString],
['params', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.svcpriority = args.priority;
record.target = args.target;
record.svcparams = args.params;
},
});
// PTR(name,target, recordModifiers...)
var PTR = recordBuilder('PTR');
// NAPTR(name,order,preference,flags,service,regexp,target, recordModifiers...)
var NAPTR = recordBuilder('NAPTR', {
args: [
['name', _.isString],
['order', _.isNumber],
['preference', _.isNumber],
['flags', _.isString],
['service', _.isString],
['regexp', _.isString],
['target', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.naptrorder = args.order;
record.naptrpreference = args.preference;
record.naptrflags = args.flags;
record.naptrservice = args.service;
record.naptrregexp = args.regexp;
record.target = args.target;
},
});
// SOA(name,ns,mbox,refresh,retry,expire,minimum, recordModifiers...)
var SOA = recordBuilder('SOA', {
args: [
['name', _.isString],
['target', _.isString],
['mbox', _.isString],
['refresh', _.isNumber],
['retry', _.isNumber],
['expire', _.isNumber],
['minttl', _.isNumber],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.target = args.target;
record.soambox = args.mbox;
record.soarefresh = args.refresh;
record.soaretry = args.retry;
record.soaexpire = args.expire;
record.soaminttl = args.minttl;
},
});
// SRV(name,priority,weight,port,target, recordModifiers...)
var SRV = recordBuilder('SRV', {
args: [
['name', _.isString],
['priority', _.isNumber],
['weight', _.isNumber],
['port', _.isNumber],
['target', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.srvpriority = args.priority;
record.srvweight = args.weight;
record.srvport = args.port;
record.target = args.target;
},
});
// SSHFP(name,algorithm,type,value, recordModifiers...)
var SSHFP = recordBuilder('SSHFP', {
args: [
['name', _.isString],
['algorithm', _.isNumber],
['fingerprint', _.isNumber],
['value', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.sshfpalgorithm = args.algorithm;
record.sshfpfingerprint = args.fingerprint;
record.target = args.value;
},
});
// name, priority, target, params
var SVCB = recordBuilder('SVCB', {
args: [
['name', _.isString],
['priority', _.isNumber],
['target', _.isString],
['params', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.svcpriority = args.priority;
record.target = args.target;
record.svcparams = args.params;
},
});
// name, usage, selector, matchingtype, certificate
var TLSA = recordBuilder('TLSA', {
args: [
['name', _.isString],
['usage', _.isNumber],
['selector', _.isNumber],
['matchingtype', _.isNumber],
['target', _.isString], // recordBuilder needs a "target" argument
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.tlsausage = args.usage;
record.tlsaselector = args.selector;
record.tlsamatchingtype = args.matchingtype;
record.target = args.target;
},
});
function isStringOrArray(x) {
return _.isString(x) || _.isArray(x);
}
// AUTOSPLIT is deprecated. It is now a no-op.
var AUTOSPLIT = {};
// TXT(name,target, recordModifiers...)
var TXT = recordBuilder('TXT', {
args: [
['name', _.isString],
['target', isStringOrArray],
],
transform: function (record, args, modifiers) {
record.name = args.name;
// Store the strings from the user verbatim.
if (_.isString(args.target)) {
record.target = args.target;
} else {
record.target = args.target.join('');
}
},
});
// Parses coordinates of the form 41°24'12.2"N 2°10'26.5"E
function parseDMSCoordinatesString(inputString) {
var lat = inputString.match(/(-?\d+).(\d+).([\d\.]+).?\ ?([NS])/);
var lon = inputString.match(/(-?\d+).(\d+).([\d\.]+).?\ ?([EW])/);
if (!lat || !lon) {
return null;
}
return {
lati: {
dg: parseInt(lat[1]),
mn: parseInt(lat[2]),
sc: parseFloat(lat[3]),
hemi: lat[4],
},
long: {
dg: parseInt(lon[1]),
mn: parseInt(lon[2]),
sc: parseFloat(lon[3]),
hemi: lon[4],
},
};
}
// // Parses coordinates of the form 25.24°S 153.15°E
function parseDMMCoordinatesString(inputString) {
var lat = inputString.match(/(-?\d+(\.\d+)?)°?\s*([NS])/i);
var lon = inputString.match(/(-?\d+(\.\d+)?)°?\s*([EW])/i);
if (!lat || !lon) {
return null;
}
var latDeg = Math.floor(parseFloat(lat[1]));
var latMin = Math.floor((parseFloat(lat[1]) - latDeg) * 60);
var latSec = Math.round(((parseFloat(lat[1]) - latDeg) * 60 - latMin) * 60);
var lonDeg = Math.floor(parseFloat(lon[1]));
var lonMin = Math.floor((parseFloat(lon[1]) - lonDeg) * 60);
var lonSec = Math.round(((parseFloat(lon[1]) - lonDeg) * 60 - lonMin) * 60);
var lati = {
dg: latDeg,
mn: latMin,
sc: latSec,
hemi: lat[3],
};
var long = {
dg: lonDeg,
mn: lonMin,
sc: lonSec,
hemi: lon[3],
};
return {
lati: lati,
long: long,
};
}
// builds a uint8 with 4 bit mantissa, 4 bit exponent from a float.
function getENotationInt(x) {
/*
9000000000cm = 9e9 == 153 (9^4 + 9) or 9<<4 + 9
800000000cm = 8e8 == 136 (8^4 + 8) or 8<<4 + 8
70000000cm = 7e7 == 119 (7^4 + 7) or 7<<4 + 7
6000000cm = 6e6 == 102 (6^4 + 6) or 6<<4 + 6
1000000cm = 1e6 == 22 (1^4 + 6) or 1<<4 + 6
500000cm = 5e5 == 85 (5^4 + 5) or 5<<4 + 5
40000cm = 4e4 == 68 (4^4 + 4) or 4<<4 + 4
3000cm = 3e3 == 51 (3^4 + 3) or 3<<4 + 3
1000cm = 1e3 == 19 (1^4 + 3) or 1<<4 + 1
200cm = 2e2 == 34 (2^4 + 2) or 2<<4 + 2
10cm = 1e1 == 17 (1^4 + 1) or 1<<4 + 1
1cm = 1e0 == 16 (1^4 + 0) or 0<<4 + 0
0cm = 0e0 == 0
*/
size = x * 100; // get cm value
// get the m^e version of size
array = size.toExponential(0).split('e+').map(Number);
// convert it to 4bit:4bit uint8
m_e = (array[0] << 4) | array[1];
return m_e;
}
// Checks LOC parameters and if all is well, renders them into a 'target' string.
// The LOC record has no target string parameter. It only renders one via String().
function locStringBuilder(record, args) {
record.name = args.name;
// technically, we don't need this part to build the text target, but
// it is a good sanity check to compare with later on down the chain
// when you're in the weeds with maths.
// Tests depend on it being present. Changes here must reflect in tests.
nsstring = '';
ewstring = '';
precisionbuffer = '';
ns = args.ns.toUpperCase();
ew = args.ew.toUpperCase();
// Handle N/S coords - can use also s1.toFixed(3)
nsstring =
args.d1.toString() +
' ' +
args.m1.toString() +
' ' +
args.s1.toString() +
' ';
var nsmatches = args.ns.match(/^([NnSs])$/);
if (nsmatches == null) {
throw v + ' is not a valid latitude modifier';
} else {
nsstring += ns + ' ';
}
// Handle E/W coords - can use also s2.toFixed(3)
ewstring =
args.d2.toString() +
' ' +
args.m2.toString() +
' ' +
args.s2.toString() +
' ';
var nsmatches = args.ew.match(/^([EeWw])$/);
if (nsmatches == null) {
throw v + ' is not a valid longitude modifier';
} else {
ewstring += ew + ' ';
}
// handle altitude, size, horizontal precision, vertical precision
precisionbuffer = args.alt.toString() + 'm';
precisionbuffer += ' ' + args.siz.toString() + 'm';
precisionbuffer += ' ' + args.hp.toString() + 'm';
precisionbuffer += ' ' + args.vp.toString() + 'm';
record.target = nsstring + ewstring + precisionbuffer;
return record;
}
// Renders LOC type internal properties from D˚M'S" parameters.
// Change anything here at your peril.
function locDMSBuilder(record, args) {
LOCEquator = 1 << 31; // RFC 1876, Section 2.
LOCPrimeMeridian = 1 << 31; // RFC 1876, Section 2.
LOCHours = 60 * 1000;
LOCDegrees = 60 * LOCHours;
LOCAltitudeBase = 100000;
lat = args.d1 * LOCDegrees + args.m1 * LOCHours + args.s1 * 1000;
lon = args.d2 * LOCDegrees + args.m2 * LOCHours + args.s2 * 1000;
if (ns == 'N') record.loclatitude = LOCEquator + lat;
// S
else record.loclatitude = LOCEquator - lat;
if (ew == 'E') record.loclongitude = LOCPrimeMeridian + lon;
// W
else record.loclongitude = LOCPrimeMeridian - lon;
// Altitude
record.localtitude = (args.alt + LOCAltitudeBase) * 100;
// Size
record.locsize = getENotationInt(args.siz);
// Horizontal Precision
m_e = args.hp;
record.lochorizpre = getENotationInt(args.hp);
// if (m_e != 0) {
// } else {
// record.lochorizpre = 22; // 10,000m default
// }
// Vertical Precision
m_e = args.vp;
record.locvertpre = getENotationInt(args.vp);
// if (m_e != 0) {
// } else {
// record.lochorizpre = 19; // 10m default
// }
}
// LOC(name,d1,m1,s1,ns,d2,m2,s2,ew,alt,siz,hp,vp, recordModifiers...)
var LOC = recordBuilder('LOC', {
args: [
['name', _.isString], //i.e. subdomain
['d1', _.isNumber], // N/S degrees
['m1', _.isNumber], // N/S minutes
['s1', _.isNumber], // N/S seconds
['ns', _.isString], // N/S
['d2', _.isNumber], // E/W degrees
['m2', _.isNumber], // E/W minutes
['s2', _.isNumber], // E/W seconds
['ew', _.isString], // E/W
['alt', _.isNumber], // altitude
['siz', _.isNumber], // size/precision
['hp', _.isNumber], // horizontal precision
['vp', _.isNumber], // vertical precision
],
transform: function (record, args, modifiers) {
record = locStringBuilder(record, args);
record = locDMSBuilder(record, args);
},
});
function ConvertDDToDMS(D, longitude) {
//stackoverflow, baby. do not re-order the rows.
return {
hemi: D < 0 ? (longitude ? 'W' : 'S') : longitude ? 'E' : 'N',
dg: 0 | (D < 0 ? (D = -D) : D),
mn: 0 | (((D += 1e-9) % 1) * 60),
sc: (0 | (((D * 60) % 1) * 60000)) / 1000,
};
}
// MX(name,priority,target, recordModifiers...)
var MX = recordBuilder('MX', {
args: [
['name', _.isString],
['priority', _.isNumber],
['target', _.isString],
],
transform: function (record, args, modifiers) {
record.name = args.name;
record.mxpreference = args.priority;
record.target = args.target;
},
});
// NS(name,target, recordModifiers...)
var NS = recordBuilder('NS');
// NAMESERVER(name,target)
function NAMESERVER(name) {
if (arguments.length != 1) {
throw 'NAMESERVER only accepts one argument for name.';
}
return function (d) {
d.nameservers.push({ name: name });
};
}
// NAMESERVER_TTL(v): Set the TTL for NAMESERVER records.
function NAMESERVER_TTL(v) {
if (_.isString(v)) {
v = stringToDuration(v);
}
return { ns_ttl: v.toString() };
}
function format_tt(transform_table) {
// Turn [[low: 1, high: 2, newBase: 3], [low: 4, high: 5, newIP: 6]]
// into "1 ~ 2 ~ 3 ~; 4 ~ 5 ~ ~ 6"
var lines = [];
for (var i = 0; i < transform_table.length; i++) {
var ip = transform_table[i];
var newIP = ip.newIP;
if (newIP) {
if (_.isArray(newIP)) {
newIP = _.map(newIP, function (i) {
return num2dot(i);
}).join(',');
} else {
newIP = num2dot(newIP);
}
}
var newBase = ip.newBase;
if (newBase) {
if (_.isArray(newBase)) {
newBase = _.map(newBase, function (i) {
return num2dot(i);
}).join(',');
} else {
newBase = num2dot(newBase);
}
}
var row = [num2dot(ip.low), num2dot(ip.high), newBase, newIP];
lines.push(row.join(' ~ '));
}
return lines.join(' ; ');
}
//function UNMANAGED(label_pattern, rType_pattern, target_pattern) {
// return function (d) {
// d.unmanaged.push({
// label_pattern: label_pattern,
// rType_pattern: rType_pattern,
// target_pattern: target_pattern,
// });
// };
//}
function DISABLE_IGNORE_SAFETY_CHECK(d) {
// This disables a safety check intended to prevent DNSControl and
// another system getting into a battle as they both try to update
// the same record over and over, back and forth. However, people
// kept asking for it so... caveat emptor!
// It only affects the current domain.
d.unmanaged_disable_safety_check = true;
}
// IGNORE(labelPattern, rtypePattern, targetPattern)
function IGNORE(labelPattern, rtypePattern, targetPattern) {
if (labelPattern === undefined) {
labelPattern = '*';
}
if (rtypePattern === undefined) {
rtypePattern = '*';
}
if (targetPattern === undefined) {
targetPattern = '*';
}
return function (d) {
d.unmanaged.push({
label_pattern: labelPattern,
rType_pattern: rtypePattern,
target_pattern: targetPattern,
});
};
}
// IGNORE_NAME(name, rTypes)
function IGNORE_NAME(name, rTypes) {
return IGNORE(name, rTypes);
}
function IGNORE_TARGET(target, rType) {
return IGNORE('*', rType, target);
}
// IMPORT_TRANSFORM(translation_table, domain)
var IMPORT_TRANSFORM = recordBuilder('IMPORT_TRANSFORM', {
args: [['translation_table'], ['domain'], ['ttl', _.isNumber]],
transform: function (record, args, modifiers) {
record.name = '@';
record.target = args.domain;
record.meta['transform_table'] = format_tt(args.translation_table);
record.ttl = args.ttl;
},
});
// PURGE()
function PURGE(d) {
d.KeepUnknown = false;
}
// NO_PURGE()
function NO_PURGE(d) {
d.KeepUnknown = true;
}
// ENSURE_ABSENT_REC()
// Usage: A("foo", "1.2.3.4", ENSURE_ABSENT_REC())
function ENSURE_ABSENT_REC() {
return function (r) {
r.ensure_absent = true;
};
}
// ENSURE_ABSENT()
// Usage: ENSURE_ABSENT(A("foo", "1.2.3.4"))
// (BROKEN. COMMENTED OUT UNTIL IT IS FIXED.)
// function ENSURE_ABSENT(r) {
// //console.log(r);
// return r;
// }
// AUTODNSSEC
// Permitted values are:
// "" Do not modify the setting (the default)
// "on" Enable AUTODNSSEC for this domain
// "off" Disable AUTODNSSEC for this domain
function AUTODNSSEC_ON(d) {
d.auto_dnssec = 'on';
}
function AUTODNSSEC_OFF(d) {
d.auto_dnssec = 'off';
}
function AUTODNSSEC(d) {
console.log(
'WARNING: AUTODNSSEC is deprecated. It is now a no-op. Please use AUTODNSSEC_ON or AUTODNSSEC_OFF. The default is to make no modifications. This message will disappear in a future release.'
);
}
/**
* @deprecated
*/
function getModifiers(args, start) {
var mods = [];
for (var i = start; i < args.length; i++) {
mods.push(args[i]);
}
return mods;
}
/**
* Record type builder
* @param {string} type Record type
* @param {string} opts.args[][0] Argument name
* @param {function=} opts.args[][1] Optional validator
* @param {function=} opts.transform Function to apply arguments to record.
* Take (record, args, modifier) as arguments. Any modifiers will be
* applied before this function. It should mutate the given record.
* @param {function=} opts.applyModifier Function to apply modifiers to the record
*/
function recordBuilder(type, opts) {
opts = _.defaults({}, opts, {
args: [['name', _.isString], ['target']],
transform: function (record, args, modifiers) {
// record will have modifiers already applied
// args will be an object for parameters defined
record.name = args.name;
if (_.isNumber(args.target)) {
record.target = num2dot(args.target);
} else {
record.target = args.target;
}
},
applyModifier: function (record, modifiers) {
for (var i = 0; i < modifiers.length; i++) {
var mod = modifiers[i];
if (_.isFunction(mod)) {
mod(record);
} else if (_.isObject(mod)) {
// convert transforms to strings
if (mod.transform && _.isArray(mod.transform)) {
mod.transform = format_tt(mod.transform);
}
_.extend(record.meta, mod);
} else {
throw 'ERROR: Unknown modifier type';
}
}
},
});
return function () {
var parsedArgs = {};
var modifiers = [];
if (arguments.length < opts.args.length) {
var argumentsList = opts.args
.map(function (item) {
return item[0];
})
.join(', ');
throw (
type +
' record requires ' +
opts.args.length +
' arguments (' +
argumentsList +
'). Only ' +
arguments.length +
' were supplied'
);
return;
}
// collect arguments
for (var i = 0; i < opts.args.length; i++) {
var argDefinition = opts.args[i];
var value = arguments[i];
if (argDefinition.length > 1) {
// run validator if supplied
if (!argDefinition[1](value)) {
throw (
type +
' record ' +
argDefinition[0] +
' argument validation failed'
);
}
}
parsedArgs[argDefinition[0]] = value;
}
// collect modifiers
for (var i = opts.args.length; i < arguments.length; i++) {
modifiers.push(arguments[i]);
}
return function (d) {
var record = {
type: type,
meta: {},
ttl: d.defaultTTL,
};
opts.applyModifier(record, modifiers);
opts.transform(record, parsedArgs, modifiers);
// Handle D_EXTEND() with subdomains.
if (
d.subdomain &&
record.type != 'CF_SINGLE_REDIRECT' &&
record.type != 'CF_REDIRECT' &&
record.type != 'CF_TEMP_REDIRECT' &&
record.type != 'CF_WORKER_ROUTE'
) {
fqdn = [d.subdomain, d.name].join('.');
record.subdomain = d.subdomain;
if (record.name == '@') {
record.subdomain = d.subdomain;
record.name = d.subdomain;
} else if (fqdn != record.name && record.type != 'PTR') {
record.subdomain = d.subdomain;
record.name += '.' + d.subdomain;
}
}
// Now we finally have the record. If it is a normal record, we add
// it to "records". If it is an ENSURE_ABSENT record, we add it to
// the ensure_absent list.
if (record.ensure_absent) {
d.recordsabsent.push(record);
} else {
d.records.push(record);
}
return record;
};
};
}
/**
* @deprecated
*/
function addRecord(d, type, name, target, mods) {
// if target is number, assume ip address. convert it.
if (_.isNumber(target)) {
target = num2dot(target);
}
var rec = {
type: type,
name: name,
target: target,
ttl: d.defaultTTL,
priority: 0,
meta: {},
};
// for each modifier, decide based on type:
// - Function: call is with the record as the argument
// - Object: merge it into the metadata
// - Number: IF MX record assume it is priority
if (mods) {
for (var i = 0; i < mods.length; i++) {
var m = mods[i];
if (_.isFunction(m)) {
m(rec);
} else if (_.isObject(m)) {
// convert transforms to strings
if (m.transform && _.isArray(m.transform)) {
m.transform = format_tt(m.transform);
}
_.extend(rec.meta, m);
_.extend(rec.meta, m);
} else {
console.log(
'WARNING: Modifier type unsupported:',
typeof m,
'(Skipping!)'
);
}
}
}
d.records.push(rec);
return rec;
}
// ip conversion functions from http://stackoverflow.com/a/8105740/121660
// via http://javascript.about.com/library/blipconvert.htm
function IP(dot) {
var d = dot.split('.');
// prettier-ignore
return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]);
}
function num2dot(num) {
if (num === undefined) {
return '';
}
if (_.isString(num)) {
return num;
}
var d = num % 256;
for (var i = 3; i > 0; i--) {
num = Math.floor(num / 256);
d = (num % 256) + '.' + d;
}
return d;
}
// Cloudflare aliases:
// Meta settings for individual records.
var CF_PROXY_OFF = { cloudflare_proxy: 'off' }; // Proxy disabled.
var CF_PROXY_ON = { cloudflare_proxy: 'on' }; // Proxy enabled.
var CF_PROXY_FULL = { cloudflare_proxy: 'full' }; // Proxy+Railgun enabled.
// Per-domain meta settings:
// Proxy default off for entire domain (the default):
var CF_PROXY_DEFAULT_OFF = { cloudflare_proxy_default: 'off' };
// Proxy default on for entire domain:
var CF_PROXY_DEFAULT_ON = { cloudflare_proxy_default: 'on' };
// UniversalSSL off for entire domain:
var CF_UNIVERSALSSL_OFF = { cloudflare_universalssl: 'off' };
// UniversalSSL on for entire domain:
var CF_UNIVERSALSSL_ON = { cloudflare_universalssl: 'on' };
// CUSTOM, PROVIDER SPECIFIC RECORD TYPES
function _validateCloudflareRedirect(value) {
if (!_.isString(value)) {
return false;
}
return value.indexOf(',') === -1;
}
var CF_REDIRECT = recordBuilder('CF_REDIRECT', {
args: [
['source', _validateCloudflareRedirect],
['destination', _validateCloudflareRedirect],
],
transform: function (record, args, modifiers) {
record.name = '@';
record.target = args.source + ',' + args.destination;
},
});
var CF_TEMP_REDIRECT = recordBuilder('CF_TEMP_REDIRECT', {
args: [
['source', _validateCloudflareRedirect],
['destination', _validateCloudflareRedirect],
],
transform: function (record, args, modifiers) {
record.name = '@';
record.target = args.source + ',' + args.destination;
},
});
var CF_WORKER_ROUTE = recordBuilder('CF_WORKER_ROUTE', {
args: [
['pattern', _validateCloudflareRedirect],
['script', _validateCloudflareRedirect],
],
transform: function (record, args, modifiers) {
record.name = '@';
record.target = args.pattern + ',' + args.script;
},
});
var URL = recordBuilder('URL');
var URL301 = recordBuilder('URL301');
var FRAME = recordBuilder('FRAME');
var NS1_URLFWD = recordBuilder('NS1_URLFWD');
var CLOUDNS_WR = recordBuilder('CLOUDNS_WR');
var PORKBUN_URLFWD = recordBuilder('PORKBUN_URLFWD');
// LOC_BUILDER_DD takes an object:
// label: The DNS label for the LOC record. (default: '@')
// x: Decimal X coordinate.
// y: Decimal Y coordinate.
// alt: Altitude in m. You imperial measurement system people are suckers for punishment.
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
function LOC_BUILDER_DD(value) {
if (!value.x && !value.y) {
throw 'LOC_BUILDER_DD requires x and y elements';
}
if (!value.label) {
value.label = '@';
}
var lati = ConvertDDToDMS(value.x, false);
var long = ConvertDDToDMS(value.y, true);
dms = { lati: lati, long: long };
return LOC_builder_push(value, dms);
}
// LOC_BUILDER_DMM_STR takes an object:
// label: The DNS label for the LOC record. (default: '@')
// str: Input string of Degrees and decimal minutes (DMM) coordinates in the form: 25.24°S 153.15°E
// alt: Altitude in m. You imperial measurement system people are suckers for punishment.
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
function LOC_BUILDER_DMM_STR(value) {
if (!value.str) {
throw 'LOC_BUILDER_DMM_STR requires a string of the form 25.24°S 153.15°E';
}
if (!value.label) {
value.label = '@';
}
var dms = parseDMMCoordinatesString(value.str);
return LOC_builder_push(value, dms);
}
// LOC_BUILDER_DMS_STR takes an object:
// label: The DNS label for the LOC record. (default: '@')
// str: Input string of degrees, minutes, and seconds (DMS) coordinates in the form: 41°24'12.2"N 2°10'26.5"E
// alt: Altitude in m. You imperial measurement system people are suckers for punishment.
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
function LOC_BUILDER_DMS_STR(value) {
if (!value.str) {
throw 'LOC_BUILDER_DMS_STR requires a string of the form 33°5131″S 151°1251″Es (or 33°51\'31"S 151°12\'51"Es)';
}
if (!value.label) {
value.label = '@';
}
var dms = parseDMSCoordinatesString(value.str);
return LOC_builder_push(value, dms);
}
// LOC_BUILDER_STR takes an object:
// label: The DNS label for the LOC record. (default: '@')
// str: Input string of degrees, minutes, and seconds (DMS) coordinates in the form: 41°24'12.2"N 2°10'26.5"E
// alt: Altitude in m. You imperial measurement system people are suckers for punishment.
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
function LOC_BUILDER_STR(value) {
if (!value.str) {
throw 'LOC_BUILDER_STR requires a string';
}
if (!value.label) {
value.label = '@';
}
var dms = parseDMMCoordinatesString(value.str);
if (!dms) dms = parseDMSCoordinatesString(value.str);
return LOC_builder_push(value, dms);
}
function LOC_builder_push(value, dms) {
r = []; // The list of records to return.
p = {}; // The metaparameters to set on the LOC record.
// rawloc = "";
// Generate a LOC record with the metaparameters.
if (value.ttl) {
if (value.alt)
r.push(
LOC(
value.label,
dms.lati.dg,
dms.lati.mn,
dms.lati.sc,
dms.lati.hemi,
dms.long.dg,
dms.long.mn,
dms.long.sc,
dms.long.hemi,
value.alt,
0,
0,
0,
p,
TTL(value.ttl)
)
);
else
r.push(
LOC(
value.label,
dms.lati.dg,
dms.lati.mn,
dms.lati.sc,
dms.lati.hemi,
dms.long.dg,
dms.long.mn,
dms.long.sc,
dms.long.hemi,
0,
0,
0,
0,
p,
TTL(value.ttl)
)
);
} else {
if (value.alt)
r.push(
LOC(
value.label,
dms.lati.dg,
dms.lati.mn,
dms.lati.sc,
dms.lati.hemi,
dms.long.dg,
dms.long.mn,
dms.long.sc,
dms.long.hemi,
value.alt,
0,
0,
0,
p
)
);
else
r.push(
LOC(
value.label,
dms.lati.dg,
dms.lati.mn,
dms.lati.sc,
dms.lati.hemi,
dms.long.dg,
dms.long.mn,
dms.long.sc,
dms.long.hemi,
0,
0,
0,
0,
p
)
);
}
return r;
}
// SPF_BUILDER takes an object:
// parts: The parts of the SPF record (to be joined with ' ').
// label: The DNS label for the primary SPF record. (default: '@')
// raw: Where (which label) to store an unaltered version of the SPF settings.
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
// split: The template for additional records to be created (default: '_spf%d')
// flatten: A list of domains to be flattened.
// overhead1: Amout of "buffer room" to reserve on the first item in the spf chain.
// txtMaxSize: The maximum size for each TXT string. Values over 255 will result in multiple strings (default: '255')
function SPF_BUILDER(value) {
if (!value.parts || value.parts.length < 2) {
throw 'SPF_BUILDER requires at least 2 elements';
}
if (!value.label) {
value.label = '@';
}
if (!value.raw && value.raw !== '') {
value.raw = '_rawspf';
}
r = []; // The list of records to return.
p = {}; // The metaparameters to set on the main TXT record.
rawspf = value.parts.join(' '); // The unaltered SPF settings.
// If flattening is requested, generate a TXT record with the raw SPF settings.
if (value.flatten && value.flatten.length > 0) {
p.flatten = value.flatten.join(',');
// Only add the raw spf record if it isn't an empty string
if (value.raw !== '') {
rp = {};
if (value.ttl) {
r.push(TXT(value.raw, rawspf, rp, TTL(value.ttl)));
} else {
r.push(TXT(value.raw, rawspf, rp));
}
}
}
// If overflow is specified, enable splitting.
if (value.overflow) {
p.split = value.overflow;
}
if (value.overhead1) {
p.overhead1 = value.overhead1;
}
if (value.txtMaxSize) {
p.txtMaxSize = value.txtMaxSize;
}
// Generate a TXT record with the metaparameters.
if (value.ttl) {
r.push(TXT(value.label, rawspf, p, TTL(value.ttl)));
} else {
r.push(TXT(value.label, rawspf, p));
}
return r;
}
// CAA_BUILDER takes an object:
// label: The DNS label for the CAA record. (default: '@')
// iodef: The contact mail address. (optional)
// iodef_critical: Boolean if sending report is required/critical. If not supported, certificate should be refused. (optional)
// issue: List of CAs which are allowed to issue certificates for the domain (creates one record for each).
// issuewild: Allowed CAs which can issue wildcard certificates for this domain. (creates one record for each)
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
function CAA_BUILDER(value) {
if (!value.label) {
value.label = '@';
}
if (value.issue && value.issue == 'none') value.issue = [';'];
if (value.issuewild && value.issuewild == 'none') value.issuewild = [';'];
if (
(!value.issue && !value.issuewild) ||
(value.issue &&
value.issue.length == 0 &&
value.issuewild &&
value.issuewild.length == 0)
) {
throw 'CAA_BUILDER requires at least one entry at issue or issuewild';
}
var CAA_TTL = function () {};
if (value.ttl) {
CAA_TTL = TTL(value.ttl);
}
r = []; // The list of records to return.
if (value.iodef) {
if (value.iodef_critical) {
r.push(
CAA(value.label, 'iodef', value.iodef, CAA_CRITICAL, CAA_TTL)
);
} else {
r.push(CAA(value.label, 'iodef', value.iodef, CAA_TTL));
}
}
if (value.issue) {
var flag = function () {};
if (value.issue_critical) {
flag = CAA_CRITICAL;
}
for (var i = 0, len = value.issue.length; i < len; i++)
r.push(CAA(value.label, 'issue', value.issue[i], flag, CAA_TTL));
}
if (value.issuewild) {
var flag = function () {};
if (value.issuewild_critical) {
flag = CAA_CRITICAL;
}
for (var i = 0, len = value.issuewild.length; i < len; i++)
r.push(
CAA(value.label, 'issuewild', value.issuewild[i], flag, CAA_TTL)
);
}
return r;
}
// DMARC_BUILDER takes an object:
// label: The DNS label for the DMARC record (_dmarc prefix is added; default: '@')
// version: The DMARC version, by default DMARC1 (optional)
// policy: The DMARC policy (p=), must be one of 'none', 'quarantine', 'reject'
// subdomainPolicy: The DMARC policy for subdomains (sp=), must be one of 'none', 'quarantine', 'reject' (optional)
// alignmentSPF: 'strict'/'s' or 'relaxed'/'r' alignment for SPF (aspf=, default: 'r')
// alignmentDKIM: 'strict'/'s' or 'relaxed'/'r' alignment for DKIM (adkim=, default: 'r')
// percent: Number between 0 and 100, percentage for which policies are applied (pct=, default: 100)
// rua: Array of aggregate report targets (optional)
// ruf: Array of failure report targets (optional)
// failureOptions: Object or string; Object containing booleans SPF and DKIM, string is passed raw (fo=, default: '0')
// failureFormat: Format in which failure reports are requested (rf=, default: 'afrf')
// reportInterval: Interval in which reports are requested (ri=)
// ttl: Input for TTL method
function DMARC_BUILDER(value) {
if (!value) {
value = {};
}
if (!value.label) {
value.label = '@';
}
if (!value.version) {
value.version = 'DMARC1';
}
var label = '_dmarc';
if (value.label !== '@') {
label += '.' + value.label;
}
if (!value.policy) {
value.policy = 'none';
}
if (
!value.policy === 'none' ||
!value.policy === 'quarantine' ||
!value.policy === 'reject'
) {
throw 'Invalid DMARC policy';
}
var record = [];
record.push('v=' + value.version);
record.push('p=' + value.policy);
// Subdomain policy
if (
!value.subdomainPolicy === 'none' ||
!value.subdomainPolicy === 'quarantine' ||
!value.subdomainPolicy === 'reject'
) {
throw 'Invalid DMARC subdomain policy';
}
if (value.subdomainPolicy) {
record.push('sp=' + value.subdomainPolicy);
}
// Alignment DKIM
if (value.alignmentDKIM) {
switch (value.alignmentDKIM) {
case 'relaxed':
value.alignmentDKIM = 'r';
break;
case 'strict':
value.alignmentDKIM = 's';
break;
case 'r':
case 's':
break;
default:
throw 'Invalid DMARC DKIM alignment policy';
}
record.push('adkim=' + value.alignmentDKIM);
}
// Alignment SPF
if (value.alignmentSPF) {
switch (value.alignmentSPF) {
case 'relaxed':
value.alignmentSPF = 'r';
break;
case 'strict':
value.alignmentSPF = 's';
break;
case 'r':
case 's':
break;
default:
throw 'Invalid DMARC DKIM alignment policy';
}
record.push('aspf=' + value.alignmentSPF);
}
// Percentage
if (value.percent) {
record.push('pct=' + value.percent);
}
// Aggregate reports
if (value.rua && value.rua.length > 0) {
record.push('rua=' + value.rua.join(','));
}
// Failure reports
if (value.ruf && value.ruf.length > 0) {
record.push('ruf=' + value.ruf.join(','));
}
// Failure reporting options
if (value.ruf && value.failureOptions) {
var fo = '0';
if (_.isObject(value.failureOptions)) {
if (value.failureOptions.DKIM) {
fo = 'd';
}
if (value.failureOptions.SPF) {
fo = 's';
}
if (value.failureOptions.DKIM && value.failureOptions.SPF) {
fo = '1';
}
} else {
fo = value.failureOptions;
}
if (fo !== '0') {
record.push('fo=' + fo);
}
}
// Failure report format
if (value.ruf && value.failureFormat) {
record.push('rf=' + value.failureFormat);
}
// Report interval
if (value.reportInterval) {
if (_.isString(value.reportInterval)) {
value.reportInterval = stringToDuration(value.reportInterval);
}
record.push('ri=' + value.reportInterval);
}
if (value.ttl) {
return TXT(label, record.join('; '), TTL(value.ttl));
}
return TXT(label, record.join('; '));
}
// Documentation of the records: https://learn.microsoft.com/en-us/microsoft-365/enterprise/external-domain-name-system-records?view=o365-worldwide
function M365_BUILDER(name, value) {
// value is optional
if (!value) {
value = {};
}
if (value.mx !== false) {
value.mx = true;
}
if (value.autodiscover !== false) {
value.autodiscover = true;
}
if (value.dkim !== false) {
value.dkim = true;
}
if (!value.label) {
value.label = '@';
}
if (!value.domainGUID) {
// Does not work with dashes in domain name.
// Microsoft uses its own, (probably) deterministic algorithm to transform these domains.
// Unfortunately, underlying algorithm is not known to us.
if (name.indexOf('-') !== -1) {
throw (
'M365_BUILDER requires domainGUID for domains with dashes: ' +
name
);
}
value.domainGUID = name.replace('.', '-');
}
if (value.dkim && !value.initialDomain) {
throw (
"M365_BUILDER requires your M365 account's initial domain to set up DKIM (default: enabled): " +
name
);
}
var r = [];
// MX (default: true)
if (value.mx) {
r.push(
MX(
value.label,
0,
value.domainGUID + '.mail.protection.outlook.com.'
)
);
}
// Autodiscover (default: true)
if (value.autodiscover) {
if ((value.label = '@')) {
r.push(CNAME('autodiscover', 'autodiscover.outlook.com.'));
} else {
r.push(
CNAME(
'autodiscover.' + value.label,
'autodiscover.outlook.com.'
)
);
}
}
// DKIM (default: true)
if (value.dkim) {
r.push(
CNAME(
'selector1._domainkey',
'selector1-' +
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
)
);
r.push(
CNAME(
'selector2._domainkey',
'selector2-' +
value.domainGUID +
'._domainkey.' +
value.initialDomain +
'.'
)
);
}
// Skype for Business (default: false)
if (value.skypeForBusiness) {
r.push(CNAME('lyncdiscover', 'webdir.online.lync.com.'));
r.push(CNAME('sip', 'sipdir.online.lync.com.'));
r.push(SRV('_sip._tls', 100, 1, 443, 'sipdir.online.lync.com.'));
r.push(
SRV(
'_sipfederationtls._tcp',
100,
1,
5061,
'sipfed.online.lync.com.'
)
);
}
// Mobile Device Management (default: false)
if (value.mdm) {
r.push(
CNAME(
'enterpriseregistration',
'enterpriseregistration.windows.net.'
)
);
r.push(
CNAME(
'enterpriseenrollment',
'enterpriseenrollment.manage.microsoft.com.'
)
);
}
return r;
}
// This is a no-op. Long TXT records are handled natively now.
function DKIM(arr) {
return arr;
}
// Function wrapper for glob() for recursively loading files.
// As the main function (in Go) is in our control anyway, all the values here are already sanity-checked.
// Note: glob() is only an internal undocumented helper function. So use it on your own risk.
function require_glob() {
arguments[2] = 'js'; // force to only include .js files.
var files = glob.apply(null, arguments);
for (var i = 0; i < files.length; i++) {
require(files[i]);
}
return files;
}
// Set default values for CLI variables
function CLI_DEFAULTS(defaults) {
for (var key in defaults) {
if (typeof this[key] === 'undefined') {
this[key] = defaults[key];
}
}
}
function FETCH() {
return fetch.apply(null, arguments).catch(PANIC);
}
// DOMAIN_ELSEWHERE is a helper macro that delegates a domain to a
// static list of nameservers. It updates the registrar (the parent)
// with a list of nameservers. This is used when we own the domain (we
// control the registrar) but something else controls the DNS records
// (often a third-party of Azure).
// Usage: DOMAIN_ELSEWHERE("example.com", REG_NAMEDOTCOM, ["ns1.foo.com", "ns2.foo.com"]);
function DOMAIN_ELSEWHERE(domain, registrar, nslist) {
D(domain, registrar, NO_PURGE);
// NB(tlim): NO_PURGE is added as a precaution since something else
// is maintaining the DNS records in that zone. In theory this is
// not needed since this domain won't have a DSP defined.
for (var i = 0; i < nslist.length; i++) {
D_EXTEND(domain, NAMESERVER(nslist[i]));
}
}
// DOMAIN_ELSEWHERE_AUTO is similar to DOMAIN_ELSEWHERE but the list of
// nameservers is queried from a DNS Service Provider.
// Usage: DOMAIN_ELSEWHERE_AUTO("example.com", REG_NAMEDOTCOM, DNS_FOO)
function DOMAIN_ELSEWHERE_AUTO(domain, registrar, dsplist) {
D(domain, registrar, NO_PURGE);
// NB(tlim): NO_PURGE is required since something else
// is maintaining the DNS records in that zone, and we have access
// to updating it (but we don't want to use it.)
for (var i = 2; i < arguments.length; i++) {
D_EXTEND(domain, DnsProvider(arguments[i]));
}
}
var END = {}; // This is null. It permits the last item to include a comma.
// D("foo.com", ...
// A(...),
// A(...),
// A(...),
// END)
// Record modifiers:
// Permit labels like "foo.bar.com.bar.com" (normally an error):
var DISABLE_REPEATED_DOMAIN_CHECK = { skip_fqdn_check: 'true' };
// D("bar.com", ...
// A("foo.bar.com", "10.1.1.1", DISABLE_REPEATED_DOMAIN_CHECK),
// )
// ============================================================
// RTYPES
// Background:
// Old-style commands: Commands built using recordBuild() are the original
// style. They all validation and pre-processing here in helpers.js. This
// seemed like a good idea at the time, but in hindsight it puts a burden on the
// developer to know both Javascript and go.
// New-style commands: Command built using rawrecordBuilder() are the new style.
// They simply pack up the arguments listed in dnsconfig.js and store them in
// .rawrecords. This is passed to the Go code, which is responsible for all
// validation, pre-processing, etc. The benefit is this minimizes the need for
// Javascript knowledge, and allows us to use the testing platform build into
// Go.
function rawrecordBuilder(type) {
return function () {
// Copy the raw args:
var rawArgs = [];
for (var i = 0; i < arguments.length; i++) {
rawArgs.push(arguments[i]);
}
return function (d) {
var record = {
type: type,
};
// Process the args: Functions are executed, objects are assumed to
// be meta and stored, strings are assumed to be args and are
// stored.
// NB(tlim): Allowing for the intermixing of args and meta seems
// bad. It might be better to simply preserve the first n items as
// args, then assume the rest are metas. That would be more similar
// to the old style functions. However at this time I can't think of
// a reason this isn't sufficient.
var processedArgs = [];
var processedMetas = [];
for (var i = 0; i < rawArgs.length; i++) {
var r = rawArgs[i];
if (_.isFunction(r)) {
r(record);
} else if (_.isObject(r)) {
processedMetas.push(r);
} else {
processedArgs.push(r);
}
}
// Store the processed args.
record.args = processedArgs;
record.metas = processedMetas;
// Add this raw record to the list of records.
d.rawrecords.push(record);
return record;
};
};
}
// PLEASE KEEP THIS LIST ALPHABETICAL!
// CLOUDFLAREAPI:
var CF_SINGLE_REDIRECT = rawrecordBuilder('CLOUDFLAREAPI_SINGLE_REDIRECT');