github.com/pmoroney/dnscontrol@v0.2.4-0.20171024134423-fad98f73f44a/pkg/js/helpers.js (about) 1 'use strict'; 2 3 var conf = { 4 registrars: [], 5 dns_providers: [], 6 domains: [], 7 }; 8 9 var defaultArgs = []; 10 11 function initialize() { 12 conf = { 13 registrars: [], 14 dns_providers: [], 15 domains: [], 16 }; 17 defaultArgs = []; 18 } 19 20 function NewRegistrar(name, type, meta) { 21 if (type) { 22 type == 'MANUAL'; 23 } 24 var reg = { name: name, type: type, meta: meta }; 25 conf.registrars.push(reg); 26 return name; 27 } 28 29 function NewDnsProvider(name, type, meta) { 30 if (typeof meta === 'object' && 'ip_conversions' in meta) { 31 meta.ip_conversions = format_tt(meta.ip_conversions); 32 } 33 var dsp = { name: name, type: type, meta: meta }; 34 conf.dns_providers.push(dsp); 35 return name; 36 } 37 38 function newDomain(name, registrar) { 39 return { 40 name: name, 41 registrar: registrar, 42 meta: {}, 43 records: [], 44 dnsProviders: {}, 45 defaultTTL: 0, 46 nameservers: [], 47 }; 48 } 49 50 function processDargs(m, domain) { 51 // for each modifier, if it is a... 52 // function: call it with domain 53 // array: process recursively 54 // object: merge it into metadata 55 if (_.isFunction(m)) { 56 m(domain); 57 } else if (_.isArray(m)) { 58 for (var j in m) { 59 processDargs(m[j], domain); 60 } 61 } else if (_.isObject(m)) { 62 _.extend(domain.meta, m); 63 } else { 64 throw 'WARNING: domain modifier type unsupported: ' + 65 typeof m + 66 ' Domain: ' + 67 domain.name; 68 } 69 } 70 71 // D(name,registrar): Create a DNS Domain. Use the parameters as records and mods. 72 function D(name, registrar) { 73 var domain = newDomain(name, registrar); 74 for (var i = 0; i < defaultArgs.length; i++) { 75 processDargs(defaultArgs[i], domain); 76 } 77 for (var i = 2; i < arguments.length; i++) { 78 var m = arguments[i]; 79 processDargs(m, domain); 80 } 81 conf.domains.push(domain); 82 } 83 84 // DEFAULTS provides a set of default arguments to apply to all future domains. 85 // Each call to DEFAULTS will clear any previous values set. 86 function DEFAULTS() { 87 defaultArgs = []; 88 for (var i = 0; i < arguments.length; i++) { 89 defaultArgs.push(arguments[i]); 90 } 91 } 92 93 // TTL(v): Set the TTL for a DNS record. 94 function TTL(v) { 95 if (_.isString(v)) { 96 v = stringToDuration(v); 97 } 98 return function(r) { 99 r.ttl = v; 100 }; 101 } 102 103 function stringToDuration(v) { 104 var matches = v.match(/^(\d+)([smhdwny]?)$/); 105 if (matches == null) { 106 throw v + ' is not a valid duration string'; 107 } 108 unit = 's'; 109 if (matches[2]) { 110 unit = matches[2]; 111 } 112 v = parseInt(matches[1]); 113 var u = { s: 1, m: 60, h: 3600 }; 114 u['d'] = u.h * 24; 115 u['w'] = u.d * 7; 116 u['n'] = u.d * 30; 117 u['y'] = u.d * 365; 118 v *= u[unit]; 119 return v; 120 } 121 122 // DefaultTTL(v): Set the default TTL for the domain. 123 function DefaultTTL(v) { 124 if (_.isString(v)) { 125 v = stringToDuration(v); 126 } 127 return function(d) { 128 d.defaultTTL = v; 129 }; 130 } 131 132 function makeCAAFlag(value) { 133 return function(record) { 134 record.caaflag |= value; 135 }; 136 } 137 138 // CAA_CRITICAL: Critical CAA flag 139 var CAA_CRITICAL = makeCAAFlag(1 << 0); 140 141 // DnsProvider("providerName", 0) 142 // nsCount of 0 means don't use or register any nameservers. 143 // nsCount not provider means use all. 144 function DnsProvider(name, nsCount) { 145 if (typeof nsCount === 'undefined') { 146 nsCount = -1; 147 } 148 return function(d) { 149 d.dnsProviders[name] = nsCount; 150 }; 151 } 152 153 // A(name,ip, recordModifiers...) 154 var A = recordBuilder('A'); 155 156 // AAAA(name,ip, recordModifiers...) 157 var AAAA = recordBuilder('AAAA'); 158 159 // ALIAS(name,target, recordModifiers...) 160 var ALIAS = recordBuilder('ALIAS'); 161 162 // CAA(name,tag,value, recordModifiers...) 163 var CAA = recordBuilder('CAA', { 164 // TODO(tlim): It should be an error if value is not 0 or 128. 165 args: [['name', _.isString], ['tag', _.isString], ['value', _.isString]], 166 transform: function(record, args, modifiers) { 167 record.name = args.name; 168 record.caatag = args.tag; 169 record.target = args.value; 170 }, 171 modifierNumber: function(record, value) { 172 record.caaflags |= value; 173 }, 174 }); 175 176 // CNAME(name,target, recordModifiers...) 177 var CNAME = recordBuilder('CNAME'); 178 179 // PTR(name,target, recordModifiers...) 180 var PTR = recordBuilder('PTR'); 181 182 // SRV(name,priority,weight,port,target, recordModifiers...) 183 var SRV = recordBuilder('SRV', { 184 args: [ 185 ['name', _.isString], 186 ['priority', _.isNumber], 187 ['weight', _.isNumber], 188 ['port', _.isNumber], 189 ['target', _.isString], 190 ], 191 transform: function(record, args, modifiers) { 192 record.name = args.name; 193 record.srvpriority = args.priority; 194 record.srvweight = args.weight; 195 record.srvport = args.port; 196 record.target = args.target; 197 }, 198 }); 199 200 // name, usage, selector, matchingtype, certificate 201 var TLSA = recordBuilder('TLSA', { 202 args: [ 203 ['name', _.isString], 204 ['usage', _.isNumber], 205 ['selector', _.isNumber], 206 ['matchingtype', _.isNumber], 207 ['target', _.isString], //recordBuilder needs a "target" argument 208 ], 209 transform: function(record, args, modifiers) { 210 record.name = args.name; 211 record.tlsausage = args.usage; 212 record.tlsaselector = args.selector; 213 record.tlsamatchingtype = args.matchingtype; 214 record.target = args.target; 215 }, 216 }); 217 218 // TXT(name,target, recordModifiers...) 219 var TXT = recordBuilder('TXT'); 220 221 // MX(name,priority,target, recordModifiers...) 222 var MX = recordBuilder('MX', { 223 args: [ 224 ['name', _.isString], 225 ['priority', _.isNumber], 226 ['target', _.isString], 227 ], 228 transform: function(record, args, modifiers) { 229 record.name = args.name; 230 record.mxpreference = args.priority; 231 record.target = args.target; 232 }, 233 }); 234 235 function checkArgs(checks, args, desc) { 236 if (args.length < checks.length) { 237 throw desc; 238 } 239 for (var i = 0; i < checks.length; i++) { 240 if (!checks[i](args[i])) { 241 throw desc + ' - argument ' + i + ' is not correct type'; 242 } 243 } 244 } 245 246 // NS(name,target, recordModifiers...) 247 var NS = recordBuilder('NS'); 248 249 // NAMESERVER(name,target) 250 function NAMESERVER(name, target) { 251 return function(d) { 252 d.nameservers.push({ name: name, target: target }); 253 }; 254 } 255 256 function format_tt(transform_table) { 257 // Turn [[low: 1, high: 2, newBase: 3], [low: 4, high: 5, newIP: 6]] 258 // into "1 ~ 2 ~ 3 ~; 4 ~ 5 ~ ~ 6" 259 var lines = []; 260 for (var i = 0; i < transform_table.length; i++) { 261 var ip = transform_table[i]; 262 var newIP = ip.newIP; 263 if (newIP) { 264 if (_.isArray(newIP)) { 265 newIP = _.map(newIP, function(i) { 266 return num2dot(i); 267 }).join(','); 268 } else { 269 newIP = num2dot(newIP); 270 } 271 } 272 var newBase = ip.newBase; 273 if (newBase) { 274 if (_.isArray(newBase)) { 275 newBase = _.map(newBase, function(i) { 276 return num2dot(i); 277 }).join(','); 278 } else { 279 newBase = num2dot(newBase); 280 } 281 } 282 var row = [num2dot(ip.low), num2dot(ip.high), newBase, newIP]; 283 lines.push(row.join(' ~ ')); 284 } 285 return lines.join(' ; '); 286 } 287 288 // IMPORT_TRANSFORM(translation_table, domain) 289 var IMPORT_TRANSFORM = recordBuilder('IMPORT_TRANSFORM', { 290 args: [['translation_table'], ['domain'], ['ttl', _.isNumber]], 291 transform: function(record, args, modifiers) { 292 record.name = '@'; 293 record.target = args.domain; 294 record.meta['transform_table'] = format_tt(args.translation_table); 295 record.ttl = args.ttl; 296 }, 297 }); 298 299 // PURGE() 300 function PURGE(d) { 301 d.KeepUnknown = false; 302 } 303 304 // NO_PURGE() 305 function NO_PURGE(d) { 306 d.KeepUnknown = true; 307 } 308 309 /** 310 * @deprecated 311 */ 312 function getModifiers(args, start) { 313 var mods = []; 314 for (var i = start; i < args.length; i++) { 315 mods.push(args[i]); 316 } 317 return mods; 318 } 319 320 /** 321 * Record type builder 322 * @param {string} type Record type 323 * @param {string} opts.args[][0] Argument name 324 * @param {function=} opts.args[][1] Optional validator 325 * @param {function=} opts.transform Function to apply arguments to record. 326 * Take (record, args, modifier) as arguments. Any modifiers will be 327 * applied before this function. It should mutate the given record. 328 * @param {function=} opts.applyModifier Function to apply modifiers to the record 329 */ 330 function recordBuilder(type, opts) { 331 opts = _.defaults({}, opts, { 332 args: [['name', _.isString], ['target']], 333 334 transform: function(record, args, modifiers) { 335 // record will have modifiers already applied 336 // args will be an object for parameters defined 337 record.name = args.name; 338 if (_.isNumber(args.target)) { 339 record.target = num2dot(args.target); 340 } else { 341 record.target = args.target; 342 } 343 }, 344 345 applyModifier: function(record, modifiers) { 346 for (var i = 0; i < modifiers.length; i++) { 347 var mod = modifiers[i]; 348 349 if (_.isFunction(mod)) { 350 mod(record); 351 } else if (_.isObject(mod)) { 352 // convert transforms to strings 353 if (mod.transform && _.isArray(mod.transform)) { 354 mod.transform = format_tt(mod.transform); 355 } 356 _.extend(record.meta, mod); 357 } else { 358 throw 'ERROR: Unknown modifier type'; 359 } 360 } 361 }, 362 }); 363 364 return function() { 365 var parsedArgs = {}; 366 var modifiers = []; 367 368 if (arguments.length < opts.args.length) { 369 var argumentsList = opts.args 370 .map(function(item) { 371 return item[0]; 372 }) 373 .join(', '); 374 throw type + 375 ' record requires ' + 376 opts.args.length + 377 ' arguments (' + 378 argumentsList + 379 '). Only ' + 380 arguments.length + 381 ' were supplied'; 382 return; 383 } 384 385 // collect arguments 386 for (var i = 0; i < opts.args.length; i++) { 387 var argDefinition = opts.args[i]; 388 var value = arguments[i]; 389 if (argDefinition.length > 1) { 390 // run validator if supplied 391 if (!argDefinition[1](value)) { 392 throw type + 393 ' record ' + 394 argDefinition[0] + 395 ' argument validation failed'; 396 } 397 } 398 parsedArgs[argDefinition[0]] = value; 399 } 400 401 // collect modifiers 402 for (var i = opts.args.length; i < arguments.length; i++) { 403 modifiers.push(arguments[i]); 404 } 405 406 return function(d) { 407 var record = { 408 type: type, 409 meta: {}, 410 ttl: d.defaultTTL, 411 }; 412 413 opts.applyModifier(record, modifiers); 414 opts.transform(record, parsedArgs, modifiers); 415 416 d.records.push(record); 417 return record; 418 }; 419 }; 420 } 421 422 /** 423 * @deprecated 424 */ 425 function addRecord(d, type, name, target, mods) { 426 // if target is number, assume ip address. convert it. 427 if (_.isNumber(target)) { 428 target = num2dot(target); 429 } 430 var rec = { 431 type: type, 432 name: name, 433 target: target, 434 ttl: d.defaultTTL, 435 priority: 0, 436 meta: {}, 437 }; 438 // for each modifier, decide based on type: 439 // - Function: call is with the record as the argument 440 // - Object: merge it into the metadata 441 // - Number: IF MX record assume it is priority 442 if (mods) { 443 for (var i = 0; i < mods.length; i++) { 444 var m = mods[i]; 445 if (_.isFunction(m)) { 446 m(rec); 447 } else if (_.isObject(m)) { 448 //convert transforms to strings 449 if (m.transform && _.isArray(m.transform)) { 450 m.transform = format_tt(m.transform); 451 } 452 _.extend(rec.meta, m); 453 _.extend(rec.meta, m); 454 } else { 455 console.log( 456 'WARNING: Modifier type unsupported:', 457 typeof m, 458 '(Skipping!)' 459 ); 460 } 461 } 462 } 463 d.records.push(rec); 464 return rec; 465 } 466 467 //ip conversion functions from http://stackoverflow.com/a/8105740/121660 468 // via http://javascript.about.com/library/blipconvert.htm 469 function IP(dot) { 470 var d = dot.split('.'); 471 // prettier-ignore 472 return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]); 473 } 474 475 function num2dot(num) { 476 if (num === undefined) { 477 return ''; 478 } 479 if (_.isString(num)) { 480 return num; 481 } 482 var d = num % 256; 483 for (var i = 3; i > 0; i--) { 484 num = Math.floor(num / 256); 485 d = num % 256 + '.' + d; 486 } 487 return d; 488 } 489 490 // Cloudflare aliases: 491 492 // Meta settings for individual records. 493 var CF_PROXY_OFF = { cloudflare_proxy: 'off' }; // Proxy disabled. 494 var CF_PROXY_ON = { cloudflare_proxy: 'on' }; // Proxy enabled. 495 var CF_PROXY_FULL = { cloudflare_proxy: 'full' }; // Proxy+Railgun enabled. 496 // Per-domain meta settings: 497 // Proxy default off for entire domain (the default): 498 var CF_PROXY_DEFAULT_OFF = { cloudflare_proxy_default: 'off' }; 499 // Proxy default on for entire domain: 500 var CF_PROXY_DEFAULT_ON = { cloudflare_proxy_default: 'on' }; 501 502 // CUSTOM, PROVIDER SPECIFIC RECORD TYPES 503 504 function _validateCloudFlareRedirect(value) { 505 if (!_.isString(value)) { 506 return false; 507 } 508 return value.indexOf(',') === -1; 509 } 510 511 var CF_REDIRECT = recordBuilder('CF_REDIRECT', { 512 args: [ 513 ['source', _validateCloudFlareRedirect], 514 ['destination', _validateCloudFlareRedirect], 515 ], 516 transform: function(record, args, modifiers) { 517 record.name = '@'; 518 record.target = args.source + ',' + args.destination; 519 }, 520 }); 521 522 var CF_TEMP_REDIRECT = recordBuilder('CF_TEMP_REDIRECT', { 523 args: [ 524 ['source', _validateCloudFlareRedirect], 525 ['destination', _validateCloudFlareRedirect], 526 ], 527 transform: function(record, args, modifiers) { 528 record.name = '@'; 529 record.target = args.source + ',' + args.destination; 530 }, 531 }); 532 533 var URL = recordBuilder('URL') 534 var URL301 = recordBuilder('URL301') 535 var FRAME = recordBuilder('FRAME')