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')