diff --git a/src/dns/wildcard.js b/src/dns/wildcard.js new file mode 100644 index 000000000..b20b01765 --- /dev/null +++ b/src/dns/wildcard.js @@ -0,0 +1,85 @@ +'use strict'; + +exports = module.exports = { + upsert: upsert, + get: get, + del: del, + waitForDns: require('./waitfordns.js'), + verifyDnsConfig: verifyDnsConfig +}; + +var assert = require('assert'), + debug = require('debug')('box:dns/manual'), + dns = require('../native-dns.js'), + DomainsError = require('../domains.js').DomainsError, + sysinfo = require('../sysinfo.js'), + util = require('util'); + +function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { + assert.strictEqual(typeof dnsConfig, 'object'); + assert.strictEqual(typeof zoneName, 'string'); + assert.strictEqual(typeof subdomain, 'string'); + assert.strictEqual(typeof type, 'string'); + assert(util.isArray(values)); + assert.strictEqual(typeof callback, 'function'); + + debug('upsert: %s for zone %s of type %s with values %j', subdomain, zoneName, type, values); + + return callback(null); +} + +function get(dnsConfig, zoneName, subdomain, type, callback) { + assert.strictEqual(typeof dnsConfig, 'object'); + assert.strictEqual(typeof zoneName, 'string'); + assert.strictEqual(typeof subdomain, 'string'); + assert.strictEqual(typeof type, 'string'); + assert.strictEqual(typeof callback, 'function'); + + callback(null, [ ]); // returning ip confuses apptask into thinking the entry already exists +} + +function del(dnsConfig, zoneName, subdomain, type, values, callback) { + assert.strictEqual(typeof dnsConfig, 'object'); + assert.strictEqual(typeof zoneName, 'string'); + assert.strictEqual(typeof subdomain, 'string'); + assert.strictEqual(typeof type, 'string'); + assert(util.isArray(values)); + assert.strictEqual(typeof callback, 'function'); + + return callback(); +} + +function verifyDnsConfig(dnsConfig, domain, zoneName, ip, callback) { + assert.strictEqual(typeof dnsConfig, 'object'); + assert.strictEqual(typeof domain, 'string'); + assert.strictEqual(typeof zoneName, 'string'); + assert.strictEqual(typeof ip, 'string'); + assert.strictEqual(typeof callback, 'function'); + + if ('hyphenatedSubdomains' in dnsConfig && typeof dnsConfig.hyphenatedSubdomains !== 'boolean') return callback(new DomainsError(DomainsError.BAD_FIELD, 'hyphenatedSubdomains must be a boolean')); + + var config = { + hyphenatedSubdomains: !!dnsConfig.hyphenatedSubdomains + }; + + // Very basic check if the nameservers can be fetched + dns.resolve(zoneName, 'NS', { timeout: 5000 }, function (error, nameservers) { + if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, 'Unable to resolve nameservers for this domain')); + if (error || !nameservers) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : 'Unable to get nameservers')); + + const separator = config.hyphenatedSubdomains ? '-' : '.'; + const fqdn = `cloudrontest${separator}${domain}`; + dns.resolve(fqdn, 'A', { server: '127.0.0.1', timeout: 5000 }, function (error, result) { + if (error && error.code === 'ENOTFOUND') return callback(new DomainsError(DomainsError.BAD_FIELD, `Unable to resolve ${fqdn}`)); + if (error || !result) return callback(new DomainsError(DomainsError.BAD_FIELD, error ? error.message : `Unable to resolve ${fqdn}`)); + + sysinfo.getPublicIp(function (error, ip) { + if (error) return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, `Failed to detect IP of this server: ${error.message}`)); + + if (result.length !== 1 || ip !== result[0]) return callback(new DomainsError(DomainsError.EXTERNAL_ERROR, `Domain resolves to ${JSON.stringify(result)} instead of ${ip}`)); + + callback(null, config); + }); + }); + }); +} diff --git a/src/domains.js b/src/domains.js index 12f40a9f8..a1dc80fa2 100644 --- a/src/domains.js +++ b/src/domains.js @@ -90,7 +90,7 @@ function api(provider) { case 'namecom': return require('./dns/namecom.js'); case 'noop': return require('./dns/noop.js'); case 'manual': return require('./dns/manual.js'); - case 'wildcard': return require('./dns/manual.js'); + case 'wildcard': return require('./dns/wildcard.js'); default: return null; } } @@ -186,9 +186,9 @@ function add(domain, zoneName, provider, dnsConfig, fallbackCertificate, tlsConf if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, 'Error getting IP:' + error.message)); verifyDnsConfig(dnsConfig, domain, zoneName, provider, ip, function (error, result) { - if (error && error.reason === DomainsError.ACCESS_DENIED) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Error adding A record. Access denied')); + if (error && error.reason === DomainsError.ACCESS_DENIED) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Incorrect configuration. Access denied')); if (error && error.reason === DomainsError.NOT_FOUND) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Zone not found')); - if (error && error.reason === DomainsError.EXTERNAL_ERROR) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Error adding A record: ' + error.message)); + if (error && error.reason === DomainsError.EXTERNAL_ERROR) return callback(new DomainsError(DomainsError.BAD_FIELD, 'Configuration error: ' + error.message)); if (error && error.reason === DomainsError.BAD_FIELD) return callback(new DomainsError(DomainsError.BAD_FIELD, error.message)); if (error && error.reason === DomainsError.INVALID_PROVIDER) return callback(new DomainsError(DomainsError.BAD_FIELD, error.message)); if (error) return callback(new DomainsError(DomainsError.INTERNAL_ERROR, error));