2017-09-16 01:12:09 +08:00
'use strict' ;
2016-08-23 08:31:50 +08:00
2020-07-10 00:52:49 +08:00
// If you edit this file, you must run `go generate` to embed this
// file in the source code.
2020-09-28 04:37:42 +08:00
2020-07-10 00:52:49 +08:00
// If you are heavily debugging this code, the "-dev" flag will
// read this file directly instead of using the output of
// `go generate`. You'll still need to run `go generate` before
// you commit the changes.
2016-08-23 08:31:50 +08:00
var conf = {
registrars : [ ] ,
2016-12-17 04:10:27 +08:00
dns _providers : [ ] ,
2017-09-16 01:12:09 +08:00
domains : [ ] ,
2017-11-15 12:14:45 +08:00
domain _names : [ ] ,
2016-08-23 08:31:50 +08:00
} ;
2016-12-17 04:10:27 +08:00
var defaultArgs = [ ] ;
2016-08-23 08:31:50 +08:00
2017-09-16 01:12:09 +08:00
function initialize ( ) {
2016-08-23 08:31:50 +08:00
conf = {
registrars : [ ] ,
2016-12-17 04:10:27 +08:00
dns _providers : [ ] ,
2017-09-16 01:12:09 +08:00
domains : [ ] ,
2016-08-23 08:31:50 +08:00
} ;
2016-12-17 04:10:27 +08:00
defaultArgs = [ ] ;
2016-08-23 08:31:50 +08:00
}
2020-08-04 21:43:02 +08:00
// 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 ;
}
2017-09-16 01:12:09 +08:00
function NewRegistrar ( name , type , meta ) {
2016-08-23 08:31:50 +08:00
if ( type ) {
2017-09-16 01:12:09 +08:00
type == 'MANUAL' ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
var reg = { name : name , type : type , meta : meta } ;
2016-08-23 08:31:50 +08:00
conf . registrars . push ( reg ) ;
return name ;
}
2016-12-17 04:10:27 +08:00
function NewDnsProvider ( name , type , meta ) {
2017-09-16 01:12:09 +08:00
if ( typeof meta === 'object' && 'ip_conversions' in meta ) {
meta . ip _conversions = format _tt ( meta . ip _conversions ) ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
var dsp = { name : name , type : type , meta : meta } ;
2016-12-17 04:10:27 +08:00
conf . dns _providers . push ( dsp ) ;
2016-08-23 08:31:50 +08:00
return name ;
}
2017-09-16 01:12:09 +08:00
function newDomain ( name , registrar ) {
return {
name : name ,
2020-10-08 02:27:33 +08:00
subdomain : '' ,
2017-09-16 01:12:09 +08:00
registrar : registrar ,
meta : { } ,
records : [ ] ,
dnsProviders : { } ,
defaultTTL : 0 ,
nameservers : [ ] ,
2020-08-18 23:14:34 +08:00
ignored _names : [ ] ,
ignored _targets : [ ] ,
2017-09-16 01:12:09 +08:00
} ;
2016-08-23 08:31:50 +08:00
}
function processDargs ( m , domain ) {
2017-09-16 01:12:09 +08:00
// 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 ) ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
} else if ( _ . isObject ( m ) ) {
_ . extend ( domain . meta , m ) ;
} else {
throw 'WARNING: domain modifier type unsupported: ' +
typeof m +
' Domain: ' +
domain . name ;
}
2016-08-23 08:31:50 +08:00
}
// D(name,registrar): Create a DNS Domain. Use the parameters as records and mods.
2017-09-16 01:12:09 +08:00
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 ++ ) {
2016-08-23 08:31:50 +08:00
var m = arguments [ i ] ;
2017-09-16 01:12:09 +08:00
processDargs ( m , domain ) ;
2016-08-23 08:31:50 +08:00
}
2017-12-07 04:56:57 +08:00
if ( conf . domain _names . indexOf ( name ) !== - 1 ) {
throw name + ' is declared more than once' ;
2017-11-15 12:14:45 +08:00
}
2017-09-16 01:12:09 +08:00
conf . domains . push ( domain ) ;
2017-11-15 12:14:45 +08:00
conf . domain _names . push ( name ) ;
2016-08-23 08:31:50 +08:00
}
2020-10-08 02:27:33 +08:00
// D_EXTEND(name): Update a DNS Domain already added with D(), or subdomain thereof
2020-08-04 21:43:02 +08:00
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.' ;
}
2020-10-08 02:27:33 +08:00
domain . obj . subdomain = name . substr ( 0 , name . length - domain . obj . name . length - 1 ) ;
2020-08-04 21:43:02 +08:00
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.
}
2020-10-08 04:03:36 +08:00
// _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
2020-11-18 20:05:26 +08:00
// domain's name. The longest match is returned.
2020-08-04 21:43:02 +08:00
function _getDomainObject ( name ) {
2020-10-26 01:29:25 +08:00
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 ] } ;
}
}
2020-08-04 21:43:02 +08:00
}
2020-10-26 01:29:25 +08:00
return domain ;
2020-08-04 21:43:02 +08:00
}
2016-12-17 04:10:27 +08:00
// DEFAULTS provides a set of default arguments to apply to all future domains.
// Each call to DEFAULTS will clear any previous values set.
2017-09-16 01:12:09 +08:00
function DEFAULTS ( ) {
2016-12-17 04:10:27 +08:00
defaultArgs = [ ] ;
2017-09-16 01:12:09 +08:00
for ( var i = 0 ; i < arguments . length ; i ++ ) {
2016-12-17 04:10:27 +08:00
defaultArgs . push ( arguments [ i ] ) ;
}
}
2016-08-23 08:31:50 +08:00
// TTL(v): Set the TTL for a DNS record.
function TTL ( v ) {
2017-09-16 01:12:09 +08:00
if ( _ . isString ( v ) ) {
2017-06-08 21:15:14 +08:00
v = stringToDuration ( v ) ;
}
2016-08-23 08:31:50 +08:00
return function ( r ) {
r . ttl = v ;
2017-09-16 01:12:09 +08:00
} ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
function stringToDuration ( v ) {
2017-06-08 21:15:14 +08:00
var matches = v . match ( /^(\d+)([smhdwny]?)$/ ) ;
2017-09-16 01:12:09 +08:00
if ( matches == null ) {
throw v + ' is not a valid duration string' ;
2017-06-08 21:15:14 +08:00
}
2017-09-16 01:12:09 +08:00
unit = 's' ;
if ( matches [ 2 ] ) {
unit = matches [ 2 ] ;
2017-06-08 21:15:14 +08:00
}
2017-09-16 01:12:09 +08:00
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 ;
2017-06-08 21:15:14 +08:00
v *= u [ unit ] ;
2017-09-16 01:12:09 +08:00
return v ;
2017-06-08 21:15:14 +08:00
}
2016-08-23 08:31:50 +08:00
// DefaultTTL(v): Set the default TTL for the domain.
function DefaultTTL ( v ) {
2017-09-16 01:12:09 +08:00
if ( _ . isString ( v ) ) {
2017-06-08 21:15:14 +08:00
v = stringToDuration ( v ) ;
}
2016-08-23 08:31:50 +08:00
return function ( d ) {
d . defaultTTL = v ;
2017-09-16 01:12:09 +08:00
} ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
function makeCAAFlag ( value ) {
return function ( record ) {
2017-08-12 02:48:43 +08:00
record . caaflag |= value ;
} ;
}
2017-07-26 02:59:40 +08:00
// CAA_CRITICAL: Critical CAA flag
2018-02-10 03:03:40 +08:00
var CAA _CRITICAL = makeCAAFlag ( 1 << 7 ) ;
2016-12-17 04:10:27 +08:00
2017-09-16 01:12:09 +08:00
// DnsProvider("providerName", 0)
2016-12-17 04:10:27 +08:00
// nsCount of 0 means don't use or register any nameservers.
// nsCount not provider means use all.
2017-09-16 01:12:09 +08:00
function DnsProvider ( name , nsCount ) {
if ( typeof nsCount === 'undefined' ) {
2016-12-17 04:10:27 +08:00
nsCount = - 1 ;
}
return function ( d ) {
d . dnsProviders [ name ] = nsCount ;
2017-09-16 01:12:09 +08:00
} ;
2016-12-17 04:10:27 +08:00
}
2016-08-23 08:31:50 +08:00
// A(name,ip, recordModifiers...)
2017-08-12 02:48:43 +08:00
var A = recordBuilder ( 'A' ) ;
2016-08-23 08:31:50 +08:00
// AAAA(name,ip, recordModifiers...)
2017-08-12 02:48:43 +08:00
var AAAA = recordBuilder ( 'AAAA' ) ;
2016-08-23 08:31:50 +08:00
2017-04-20 03:13:28 +08:00
// ALIAS(name,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var ALIAS = recordBuilder ( 'ALIAS' ) ;
2017-04-20 03:13:28 +08:00
2020-03-03 00:25:42 +08:00
// 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 ;
}
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
// R53_ALIAS(name, target, type, recordModifiers...)
var R53 _ALIAS = recordBuilder ( 'R53_ALIAS' , {
2020-02-23 01:07:10 +08:00
args : [
[ 'name' , _ . isString ] ,
[ 'type' , validateR53AliasType ] ,
[ 'target' , _ . isString ] ,
] ,
transform : function ( record , args , modifiers ) {
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
record . name = args . name ;
record . target = args . target ;
if ( _ . isObject ( record . r53 _alias ) ) {
record . r53 _alias [ 'type' ] = args . type ;
} else {
2020-02-23 01:07:10 +08:00
record . r53 _alias = { type : args . type } ;
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
}
} ,
} ) ;
// R53_ZONE(zone_id)
function R53 _ZONE ( zone _id ) {
2020-02-23 01:07:10 +08:00
return function ( r ) {
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
if ( _ . isObject ( r . r53 _alias ) ) {
r . r53 _alias [ 'zone_id' ] = zone _id ;
} else {
2020-02-23 01:07:10 +08:00
r . r53 _alias = { zone _id : zone _id } ;
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
}
} ;
}
function validateR53AliasType ( value ) {
if ( ! _ . isString ( value ) ) {
return false ;
}
2020-02-23 01:07:10 +08:00
return (
[
'A' ,
'AAAA' ,
'CNAME' ,
'CAA' ,
'MX' ,
'TXT' ,
'PTR' ,
'SPF' ,
'SRV' ,
'NAPTR' ,
2020-03-03 00:25:42 +08:00
] . indexOf ( value ) !== - 1
2020-02-23 01:07:10 +08:00
) ;
ROUTE53: Support Route53's ALIAS record type (#239) (#301)
* Stable comparison of metadata (#239)
Iterating over a map in Go never produces twice the same ordering.
Thus when comparing two metadata map with more than one key, the
`differ` is always finding differences.
To properly compare records metadata, we need to iterate the maps
in a deterministic way.
Signed-off-by: Brice Figureau <brice@daysofwonder.com>
* Support for Route53 ALIAS record type (#239)
Route53 ALIAS doesn't behave like a regular ALIAS, and is much more
limited as its target can only be some specific AWS resources or
another record in the same zone.
According to #239, this change adds a new directive R53_ALIAS which
implements this specific alias. This record type can only be used
with the Route53 provider.
This directive usage looks like this:
```js
D("example.com", REGISTRAR, DnsProvider("ROUTE53"),
R53_ALIAS("foo1", "A", "bar") // record in same zone
R53_ALIAS("foo2", "A",
"blahblah.elasticloadbalancing.us-west-1.amazonaws.com",
R53_ZONE('Z368ELLRRE2KJ0')) // ELB in us-west-1
```
Unfortunately, Route53 requires indicating the hosted zone id
where the target is defined (those are listed in AWS documentation,
see the R53_ALIAS documentation for links).
2018-01-16 18:53:12 +08:00
}
2017-07-26 02:59:40 +08:00
// CAA(name,tag,value, recordModifiers...)
2017-08-12 02:48:43 +08:00
var CAA = recordBuilder ( 'CAA' , {
2017-09-16 01:12:09 +08:00
// TODO(tlim): It should be an error if value is not 0 or 128.
2020-02-23 01:07:10 +08:00
args : [
[ 'name' , _ . isString ] ,
[ 'tag' , _ . isString ] ,
[ 'value' , _ . isString ] ,
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
2017-08-12 02:48:43 +08:00
record . name = args . name ;
record . caatag = args . tag ;
record . target = args . value ;
} ,
2017-09-16 01:12:09 +08:00
modifierNumber : function ( record , value ) {
2017-08-12 02:48:43 +08:00
record . caaflags |= value ;
} ,
} ) ;
2017-04-20 03:13:28 +08:00
2016-08-23 08:31:50 +08:00
// CNAME(name,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var CNAME = recordBuilder ( 'CNAME' ) ;
2016-08-23 08:31:50 +08:00
2020-05-30 22:40:21 +08:00
// 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 ;
} ,
} ) ;
2017-07-06 22:18:15 +08:00
// PTR(name,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var PTR = recordBuilder ( 'PTR' ) ;
2017-07-06 22:18:15 +08:00
2019-03-28 22:40:13 +08:00
// 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 ;
} ,
} ) ;
2021-05-05 03:47:26 +08:00
// SOA(name,ns,mbox,serial,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 ;
} ,
} ) ;
2017-07-20 03:53:40 +08:00
// SRV(name,priority,weight,port,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var SRV = recordBuilder ( 'SRV' , {
args : [
[ 'name' , _ . isString ] ,
[ 'priority' , _ . isNumber ] ,
[ 'weight' , _ . isNumber ] ,
[ 'port' , _ . isNumber ] ,
[ 'target' , _ . isString ] ,
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
2017-08-12 02:48:43 +08:00
record . name = args . name ;
record . srvpriority = args . priority ;
record . srvweight = args . weight ;
record . srvport = args . port ;
record . target = args . target ;
} ,
} ) ;
2017-07-20 03:53:40 +08:00
2019-01-29 06:26:20 +08:00
// 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 ;
} ,
} ) ;
2017-09-15 21:03:29 +08:00
// name, usage, selector, matchingtype, certificate
var TLSA = recordBuilder ( 'TLSA' , {
args : [
[ 'name' , _ . isString ] ,
[ 'usage' , _ . isNumber ] ,
[ 'selector' , _ . isNumber ] ,
[ 'matchingtype' , _ . isNumber ] ,
2018-01-10 01:53:16 +08:00
[ 'target' , _ . isString ] , // recordBuilder needs a "target" argument
2017-09-15 21:03:29 +08:00
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
2017-09-15 21:03:29 +08:00
record . name = args . name ;
record . tlsausage = args . usage ;
record . tlsaselector = args . selector ;
record . tlsamatchingtype = args . matchingtype ;
record . target = args . target ;
} ,
} ) ;
2018-01-05 08:19:35 +08:00
function isStringOrArray ( x ) {
2018-01-07 04:13:22 +08:00
return _ . isString ( x ) || _ . isArray ( x ) ;
2018-01-05 08:19:35 +08:00
}
2020-11-18 20:05:26 +08:00
2021-03-08 02:19:22 +08:00
// AUTOSPLIT is deprecated. It is now a no-op.
var AUTOSPLIT = { } ;
2020-11-18 20:05:26 +08:00
2016-08-23 08:31:50 +08:00
// TXT(name,target, recordModifiers...)
2018-01-07 04:13:22 +08:00
var TXT = recordBuilder ( 'TXT' , {
2020-02-23 01:07:10 +08:00
args : [
[ 'name' , _ . isString ] ,
[ 'target' , isStringOrArray ] ,
] ,
2018-01-07 04:13:22 +08:00
transform : function ( record , args , modifiers ) {
record . name = args . name ;
2021-03-08 02:19:22 +08:00
// Store the strings from the user verbatim.
2018-01-07 04:13:22 +08:00
if ( _ . isString ( args . target ) ) {
record . txtstrings = [ args . target ] ;
2021-03-08 02:19:22 +08:00
record . target = args . target ; // Overwritten by the Go code
2018-01-07 04:13:22 +08:00
} else {
record . txtstrings = args . target ;
2021-03-08 02:19:22 +08:00
record . target = args . target . join ( "" ) ; // Overwritten by the Go code
2018-01-07 04:13:22 +08:00
}
} ,
2018-01-05 08:19:35 +08:00
} ) ;
2016-08-23 08:31:50 +08:00
// MX(name,priority,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var MX = recordBuilder ( 'MX' , {
args : [
[ 'name' , _ . isString ] ,
[ 'priority' , _ . isNumber ] ,
[ 'target' , _ . isString ] ,
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
2017-08-12 02:48:43 +08:00
record . name = args . name ;
record . mxpreference = args . priority ;
record . target = args . target ;
} ,
} ) ;
2016-08-23 08:31:50 +08:00
// NS(name,target, recordModifiers...)
2017-08-12 02:48:43 +08:00
var NS = recordBuilder ( 'NS' ) ;
2016-08-23 08:31:50 +08:00
// NAMESERVER(name,target)
2018-03-22 23:52:52 +08:00
function NAMESERVER ( name ) {
2020-02-23 01:07:10 +08:00
if ( arguments . length != 1 ) {
throw 'NAMESERVER only accepts one argument for name.' ;
2018-03-22 23:52:52 +08:00
}
2016-08-23 08:31:50 +08:00
return function ( d ) {
2018-03-22 23:52:52 +08:00
d . nameservers . push ( { name : name } ) ;
2017-09-16 01:12:09 +08:00
} ;
2016-08-23 08:31:50 +08:00
}
2018-09-04 22:57:11 +08:00
// NAMESERVER_TTL(v): Set the TTL for NAMESERVER records.
function NAMESERVER _TTL ( v ) {
if ( _ . isString ( v ) ) {
v = stringToDuration ( v ) ;
}
2020-02-23 01:07:10 +08:00
return { ns _ttl : v . toString ( ) } ;
2018-09-04 22:57:11 +08:00
}
2016-08-23 08:31:50 +08:00
function format _tt ( transform _table ) {
2017-09-16 01:12:09 +08:00
// 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 ) ;
}
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
var newBase = ip . newBase ;
if ( newBase ) {
if ( _ . isArray ( newBase ) ) {
newBase = _ . map ( newBase , function ( i ) {
return num2dot ( i ) ;
} ) . join ( ',' ) ;
} else {
newBase = num2dot ( newBase ) ;
}
2016-09-28 01:35:28 +08:00
}
2017-09-16 01:12:09 +08:00
var row = [ num2dot ( ip . low ) , num2dot ( ip . high ) , newBase , newIP ] ;
lines . push ( row . join ( ' ~ ' ) ) ;
2016-09-28 01:35:28 +08:00
}
2017-09-16 01:12:09 +08:00
return lines . join ( ' ; ' ) ;
2016-08-23 08:31:50 +08:00
}
2018-01-16 04:39:29 +08:00
// IGNORE(name)
function IGNORE ( name ) {
2020-08-18 23:14:34 +08:00
// deprecated, use IGNORE_NAME
return IGNORE _NAME ( name ) ;
}
// IGNORE_NAME(name)
function IGNORE _NAME ( name ) {
2020-02-23 01:07:10 +08:00
return function ( d ) {
2020-08-18 23:14:34 +08:00
d . ignored _names . push ( name ) ;
2018-01-16 04:39:29 +08:00
} ;
}
2021-04-13 20:59:47 +08:00
var IGNORE _NAME _DISABLE _SAFETY _CHECK = {
ignore _name _disable _safety _check : "true"
// This disables a safety check intended to prevent:
// 1. Two owners toggling a record between two settings.
// 2. The other owner wiping all records at this label, which won't
// be noticed until the next time dnscontrol is run.
// See https://github.com/StackExchange/dnscontrol/issues/1106
} ;
2020-08-18 23:14:34 +08:00
// IGNORE_TARGET(target)
function IGNORE _TARGET ( target , rType ) {
return function ( d ) {
d . ignored _targets . push ( { pattern : target , type : rType } ) ;
} ;
}
2016-08-23 08:31:50 +08:00
// IMPORT_TRANSFORM(translation_table, domain)
2017-08-12 02:48:43 +08:00
var IMPORT _TRANSFORM = recordBuilder ( 'IMPORT_TRANSFORM' , {
2017-09-16 01:12:09 +08:00
args : [ [ 'translation_table' ] , [ 'domain' ] , [ 'ttl' , _ . isNumber ] ] ,
transform : function ( record , args , modifiers ) {
2017-08-12 02:48:43 +08:00
record . name = '@' ;
record . target = args . domain ;
record . meta [ 'transform_table' ] = format _tt ( args . translation _table ) ;
record . ttl = args . ttl ;
} ,
} ) ;
2016-08-23 08:31:50 +08:00
// PURGE()
function PURGE ( d ) {
2017-09-16 01:12:09 +08:00
d . KeepUnknown = false ;
2016-08-23 08:31:50 +08:00
}
// NO_PURGE()
function NO _PURGE ( d ) {
2017-09-16 01:12:09 +08:00
d . KeepUnknown = true ;
2016-08-23 08:31:50 +08:00
}
2020-09-28 04:37:42 +08:00
// 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" ;
}
2020-02-22 20:09:31 +08:00
function AUTODNSSEC ( d ) {
2020-09-28 04:37:42 +08:00
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."
) ;
2020-02-22 20:09:31 +08:00
}
2017-08-12 02:48:43 +08:00
/ * *
* @ deprecated
* /
2017-09-16 01:12:09 +08:00
function getModifiers ( args , start ) {
2016-08-23 08:31:50 +08:00
var mods = [ ] ;
2017-09-16 01:12:09 +08:00
for ( var i = start ; i < args . length ; i ++ ) {
mods . push ( args [ i ] ) ;
2016-08-23 08:31:50 +08:00
}
return mods ;
}
2017-08-12 02:48:43 +08:00
/ * *
* 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
* /
2017-09-16 01:12:09 +08:00
function recordBuilder ( type , opts ) {
2017-08-12 02:48:43 +08:00
opts = _ . defaults ( { } , opts , {
2017-09-16 01:12:09 +08:00
args : [ [ 'name' , _ . isString ] , [ 'target' ] ] ,
2017-08-12 02:48:43 +08:00
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 {
2017-09-16 01:12:09 +08:00
throw 'ERROR: Unknown modifier type' ;
2017-08-12 02:48:43 +08:00
}
}
} ,
} ) ;
2017-09-16 01:12:09 +08:00
return function ( ) {
2017-08-12 02:48:43 +08:00
var parsedArgs = { } ;
var modifiers = [ ] ;
if ( arguments . length < opts . args . length ) {
2017-09-16 01:12:09 +08:00
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' ;
2017-08-12 02:48:43 +08:00
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
2017-09-16 01:12:09 +08:00
if ( ! argDefinition [ 1 ] ( value ) ) {
throw type +
' record ' +
argDefinition [ 0 ] +
' argument validation failed' ;
2017-08-12 02:48:43 +08:00
}
}
parsedArgs [ argDefinition [ 0 ] ] = value ;
}
// collect modifiers
for ( var i = opts . args . length ; i < arguments . length ; i ++ ) {
modifiers . push ( arguments [ i ] ) ;
}
2017-09-16 01:12:09 +08:00
return function ( d ) {
2017-08-12 02:48:43 +08:00
var record = {
type : type ,
meta : { } ,
ttl : d . defaultTTL ,
} ;
opts . applyModifier ( record , modifiers ) ;
opts . transform ( record , parsedArgs , modifiers ) ;
2020-10-08 02:27:33 +08:00
// Handle D_EXTEND() with subdomains.
2020-12-03 21:33:37 +08:00
if ( d . subdomain &&
record . type != 'CF_REDIRECT' &&
record . type != 'CF_TEMP_REDIRECT' ) {
fqdn = [ d . subdomain , d . name ] . join ( "." )
2020-10-08 02:27:33 +08:00
record . subdomain = d . subdomain ;
if ( record . name == '@' ) {
2020-12-03 21:33:37 +08:00
record . subdomain = d . subdomain ;
2020-10-08 02:27:33 +08:00
record . name = d . subdomain ;
2020-12-03 21:33:37 +08:00
} else if ( fqdn != record . name && record . type != 'PTR' ) {
record . subdomain = d . subdomain ;
2020-10-08 02:27:33 +08:00
record . name += '.' + d . subdomain ;
}
}
2017-08-12 02:48:43 +08:00
d . records . push ( record ) ;
return record ;
} ;
} ;
}
/ * *
* @ deprecated
* /
2017-09-16 01:12:09 +08:00
function addRecord ( d , type , name , target , mods ) {
2016-08-23 08:31:50 +08:00
// if target is number, assume ip address. convert it.
if ( _ . isNumber ( target ) ) {
target = num2dot ( target ) ;
}
2017-09-16 01:12:09 +08:00
var rec = {
type : type ,
name : name ,
target : target ,
ttl : d . defaultTTL ,
priority : 0 ,
meta : { } ,
} ;
2016-08-23 08:31:50 +08:00
// 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
2017-07-20 03:53:40 +08:00
if ( mods ) {
2017-09-16 01:12:09 +08:00
for ( var i = 0 ; i < mods . length ; i ++ ) {
var m = mods [ i ] ;
2017-07-20 03:53:40 +08:00
if ( _ . isFunction ( m ) ) {
m ( rec ) ;
} else if ( _ . isObject ( m ) ) {
2018-01-10 01:53:16 +08:00
// convert transforms to strings
2017-09-16 01:12:09 +08:00
if ( m . transform && _ . isArray ( m . transform ) ) {
m . transform = format _tt ( m . transform ) ;
}
_ . extend ( rec . meta , m ) ;
_ . extend ( rec . meta , m ) ;
2017-07-20 03:53:40 +08:00
} else {
2017-09-16 01:12:09 +08:00
console . log (
'WARNING: Modifier type unsupported:' ,
typeof m ,
'(Skipping!)'
) ;
2017-07-20 03:53:40 +08:00
}
}
}
d . records . push ( rec ) ;
return rec ;
}
2018-01-10 01:53:16 +08:00
// ip conversion functions from http://stackoverflow.com/a/8105740/121660
2016-08-23 08:31:50 +08:00
// via http://javascript.about.com/library/blipconvert.htm
2017-09-16 01:12:09 +08:00
function IP ( dot ) {
2016-08-23 08:31:50 +08:00
var d = dot . split ( '.' ) ;
2017-09-16 01:12:09 +08:00
// prettier-ignore
return ( ( ( ( ( ( + d [ 0 ] ) * 256 ) + ( + d [ 1 ] ) ) * 256 ) + ( + d [ 2 ] ) ) * 256 ) + ( + d [ 3 ] ) ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
function num2dot ( num ) {
if ( num === undefined ) {
return '' ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
if ( _ . isString ( num ) ) {
return num ;
2016-08-23 08:31:50 +08:00
}
2017-09-16 01:12:09 +08:00
var d = num % 256 ;
for ( var i = 3 ; i > 0 ; i -- ) {
num = Math . floor ( num / 256 ) ;
2020-02-23 01:07:10 +08:00
d = ( num % 256 ) + '.' + d ;
2016-08-23 08:31:50 +08:00
}
return d ;
}
2017-05-11 13:02:57 +08:00
// Cloudflare aliases:
2017-05-16 04:28:26 +08:00
// Meta settings for individual records.
2017-09-16 01:12:09 +08:00
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.
2017-05-16 04:28:26 +08:00
// Per-domain meta settings:
// Proxy default off for entire domain (the default):
2017-09-16 01:12:09 +08:00
var CF _PROXY _DEFAULT _OFF = { cloudflare _proxy _default : 'off' } ;
2017-05-16 04:28:26 +08:00
// Proxy default on for entire domain:
2017-09-16 01:12:09 +08:00
var CF _PROXY _DEFAULT _ON = { cloudflare _proxy _default : 'on' } ;
2019-06-13 19:32:54 +08:00
// UniversalSSL off for entire domain:
var CF _UNIVERSALSSL _OFF = { cloudflare _universalssl : 'off' } ;
// UniversalSSL on for entire domain:
var CF _UNIVERSALSSL _ON = { cloudflare _universalssl : 'on' } ;
2017-05-26 08:38:48 +08:00
2017-05-20 02:15:57 +08:00
// CUSTOM, PROVIDER SPECIFIC RECORD TYPES
2017-08-12 02:48:43 +08:00
2019-05-23 21:29:21 +08:00
function _validateCloudflareRedirect ( value ) {
2017-09-16 01:12:09 +08:00
if ( ! _ . isString ( value ) ) {
2017-08-12 02:48:43 +08:00
return false ;
}
2017-09-16 01:12:09 +08:00
return value . indexOf ( ',' ) === - 1 ;
2017-08-12 02:48:43 +08:00
}
2017-09-16 01:12:09 +08:00
var CF _REDIRECT = recordBuilder ( 'CF_REDIRECT' , {
2017-08-12 02:48:43 +08:00
args : [
2019-05-23 21:29:21 +08:00
[ 'source' , _validateCloudflareRedirect ] ,
[ 'destination' , _validateCloudflareRedirect ] ,
2017-08-12 02:48:43 +08:00
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
record . name = '@' ;
record . target = args . source + ',' + args . destination ;
2017-08-12 02:48:43 +08:00
} ,
} ) ;
2017-09-16 01:12:09 +08:00
var CF _TEMP _REDIRECT = recordBuilder ( 'CF_TEMP_REDIRECT' , {
2017-08-12 02:48:43 +08:00
args : [
2019-05-23 21:29:21 +08:00
[ 'source' , _validateCloudflareRedirect ] ,
[ 'destination' , _validateCloudflareRedirect ] ,
2017-08-12 02:48:43 +08:00
] ,
2017-09-16 01:12:09 +08:00
transform : function ( record , args , modifiers ) {
record . name = '@' ;
record . target = args . source + ',' + args . destination ;
2017-08-12 02:48:43 +08:00
} ,
} ) ;
2017-10-24 00:54:31 +08:00
2017-12-07 04:56:57 +08:00
var URL = recordBuilder ( 'URL' ) ;
var URL301 = recordBuilder ( 'URL301' ) ;
var FRAME = recordBuilder ( 'FRAME' ) ;
2021-02-11 00:57:15 +08:00
var NS1 _URLFWD = recordBuilder ( 'NS1_URLFWD' ) ;
2017-12-07 04:50:21 +08:00
// 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: '@')
2017-12-07 04:56:57 +08:00
// raw: Where (which label) to store an unaltered version of the SPF settings.
2019-05-23 21:25:06 +08:00
// ttl: The time for TTL, integer or string. (default: not defined, using DefaultTTL)
2017-12-07 04:50:21 +08:00
// split: The template for additional records to be created (default: '_spf%d')
// flatten: A list of domains to be flattened.
2020-07-10 00:52:49 +08:00
// overhead1: Amout of "buffer room" to reserve on the first item in the spf chain.
2020-08-01 01:28:13 +08:00
// txtMaxSize: The maximum size for each TXT string. Values over 255 will result in multiple strings (default: '255')
2017-12-07 04:50:21 +08:00
function SPF _BUILDER ( value ) {
2017-12-07 04:56:57 +08:00
if ( ! value . parts || value . parts . length < 2 ) {
throw 'SPF_BUILDER requires at least 2 elements' ;
}
if ( ! value . label ) {
value . label = '@' ;
}
2020-07-31 22:40:22 +08:00
if ( ! value . raw && value . raw !== '' ) {
2017-12-07 04:56:57 +08:00
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 ( ',' ) ;
2020-07-31 22:40:22 +08:00
// Only add the raw spf record if it isn't an empty string
if ( value . raw !== '' ) {
2020-11-18 20:05:26 +08:00
rp = { } ;
2020-07-31 22:40:22 +08:00
if ( value . ttl ) {
2020-11-18 20:05:26 +08:00
r . push ( TXT ( value . raw , rawspf , rp , TTL ( value . ttl ) ) ) ;
2020-07-31 22:40:22 +08:00
} else {
2020-11-18 20:05:26 +08:00
r . push ( TXT ( value . raw , rawspf , rp ) ) ;
2020-07-31 22:40:22 +08:00
}
2019-05-23 21:25:06 +08:00
}
2017-12-07 04:56:57 +08:00
}
// If overflow is specified, enable splitting.
if ( value . overflow ) {
p . split = value . overflow ;
}
2020-07-10 00:52:49 +08:00
if ( value . overhead1 ) {
p . overhead1 = value . overhead1 ;
}
2020-08-01 01:28:13 +08:00
if ( value . txtMaxSize ) {
p . txtMaxSize = value . txtMaxSize ;
}
2017-12-07 04:56:57 +08:00
// Generate a TXT record with the metaparameters.
2019-05-23 21:25:06 +08:00
if ( value . ttl ) {
r . push ( TXT ( value . label , rawspf , p , TTL ( value . ttl ) ) ) ;
} else {
r . push ( TXT ( value . label , rawspf , p ) ) ;
}
2017-12-07 04:56:57 +08:00
return r ;
2017-12-07 04:50:21 +08:00
}
2018-01-05 10:17:08 +08:00
2019-05-18 23:10:18 +08:00
// 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)
function CAA _BUILDER ( value ) {
if ( ! value . label ) {
value . label = '@' ;
}
2020-02-23 01:07:10 +08:00
if ( value . issue && value . issue == 'none' ) value . issue = [ ';' ] ;
if ( value . issuewild && value . issuewild == 'none' ) value . issuewild = [ ';' ] ;
2019-05-18 23:10:18 +08:00
2020-02-23 01:07:10 +08:00
if (
( ! value . issue && ! value . issuewild ) ||
( value . issue &&
value . issue . length == 0 &&
2020-03-03 00:25:42 +08:00
value . issuewild &&
value . issuewild . length == 0 )
2020-02-23 01:07:10 +08:00
) {
2019-05-18 23:10:18 +08:00
throw 'CAA_BUILDER requires at least one entry at issue or issuewild' ;
}
r = [ ] ; // The list of records to return.
if ( value . iodef ) {
if ( value . iodef _critical ) {
2020-02-23 01:07:10 +08:00
r . push ( CAA ( value . label , 'iodef' , value . iodef , CAA _CRITICAL ) ) ;
2019-05-18 23:10:18 +08:00
} else {
2020-02-23 01:07:10 +08:00
r . push ( CAA ( value . label , 'iodef' , value . iodef ) ) ;
2019-05-18 23:10:18 +08:00
}
}
if ( value . issue )
for ( var i = 0 , len = value . issue . length ; i < len ; i ++ )
2020-02-23 01:07:10 +08:00
r . push ( CAA ( value . label , 'issue' , value . issue [ i ] ) ) ;
2019-05-18 23:10:18 +08:00
if ( value . issuewild )
for ( var i = 0 , len = value . issuewild . length ; i < len ; i ++ )
2020-02-23 01:07:10 +08:00
r . push ( CAA ( value . label , 'issuewild' , value . issuewild [ i ] ) ) ;
2019-05-18 23:10:18 +08:00
return r ;
}
2021-03-08 21:50:14 +08:00
// DMARC_BUILDER takes an object:
// label: The DNS label for the DMARC record (_dmarc prefix is added; default: '@')
// 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 = '@' ;
}
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 = [ 'v=DMARC1' ] ;
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 && value . percent != 100 ) {
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 && value . failureFormat !== 'afrf' ) {
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 ( '; ' ) ) ;
}
2021-03-08 04:01:03 +08:00
// This is a no-op. Long TXT records are handled natively now.
2018-01-05 10:17:08 +08:00
function DKIM ( arr ) {
2021-03-08 04:01:03 +08:00
return arr ;
2018-01-05 10:17:08 +08:00
}
2020-08-20 02:00:40 +08:00
// 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 ( i = 0 ; i < files . length ; i ++ ) {
require ( files [ i ] ) ;
}
return files
}
2020-10-27 22:43:00 +08:00
// Set default values for CLI variables
function CLI _DEFAULTS ( defaults ) {
for ( var key in defaults ) {
if ( typeof this [ key ] === "undefined" ) {
this [ key ] = defaults [ key ]
}
}
}
2021-01-06 23:45:32 +08:00
function FETCH ( ) {
return fetch . apply ( null , arguments ) . catch ( PANIC ) ;
}