diff --git a/src/cloudron.js b/src/cloudron.js index d917c1e03..111b539b0 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -568,6 +568,7 @@ function readDkimPublicKeySync() { } // NOTE: if you change the SPF record here, be sure the wait check in mailer.js +// https://agari.zendesk.com/hc/en-us/articles/202952749-How-long-can-my-SPF-record-be- function txtRecordsWithSpf(callback) { assert.strictEqual(typeof callback, 'function'); @@ -576,21 +577,25 @@ function txtRecordsWithSpf(callback) { debug('txtRecordsWithSpf: current txt records - %j', txtRecords); - var i, validSpf; + var i, matches, validSpf; for (i = 0; i < txtRecords.length; i++) { - if (txtRecords[i].indexOf('"v=spf1 ') !== 0) continue; // not SPF + matches = txtRecords[i].match(/^("?v=spf1) /); // DO backend may return without quotes + if (matches === null) continue; - validSpf = txtRecords[i].indexOf(' a:' + config.adminFqdn() + ' ') !== -1; - break; + // this won't work if the entry is arbitrarily "split" across quoted strings + validSpf = txtRecords[i].indexOf('a:' + config.adminFqdn()) !== -1; + break; // there can only be one SPF record } if (validSpf) return callback(null, null); - if (i == txtRecords.length) { - txtRecords[i] = '"v=spf1 a:' + config.adminFqdn() + ' ~all"'; - } else { - txtRecords[i] = '"v=spf1 a:' + config.adminFqdn() + ' ' + txtRecords[i].slice('"v=spf1 '.length); + if (!matches) { // no spf record was found, create one + txtRecords.push('"v=spf1 a:' + config.adminFqdn() + ' ~all"'); + debug('txtRecordsWithSpf: adding txt record'); + } else { // just add ourself + txtRecords[i] = matches[1] + ' a:' + config.adminFqdn() + txtRecords[i].slice(matches[0].length); + debug('txtRecordsWithSpf: inserting txt record'); } return callback(null, txtRecords); diff --git a/src/routes/test/settings-test.js b/src/routes/test/settings-test.js index e912041b7..f74888afa 100644 --- a/src/routes/test/settings-test.js +++ b/src/routes/test/settings-test.js @@ -735,7 +735,7 @@ describe('Settings API', function () { dnsAnswerQueue[mxDomain].MX = [ { priority: '10', exchange: config.mailFqdn() } ]; dnsAnswerQueue[dmarcDomain].TXT = [['v=DMARC1; p=reject; pct=100']]; dnsAnswerQueue[dkimDomain].TXT = [['v=DKIM1;', 't=s;', 'p=' + cloudron.readDkimPublicKeySync()]]; - dnsAnswerQueue[spfDomain].TXT = [['v=spf1', 'a:' + config.adminFqdn(), '~all']]; + dnsAnswerQueue[spfDomain].TXT = [['v=spf1', ' a:' + config.adminFqdn(), ' ~all']]; superagent.get(SERVER_URL + '/api/v1/settings/email_dns_records') .query({ access_token: token }) diff --git a/src/settings.js b/src/settings.js index 3e2015b07..876495964 100644 --- a/src/settings.js +++ b/src/settings.js @@ -182,6 +182,7 @@ function getEmailDnsRecords(callback) { status: false }; + // https://agari.zendesk.com/hc/en-us/articles/202952749-How-long-can-my-SPF-record-be- dns.resolve(records.spf.domain, records.spf.type, function (error, txtRecords) { if (error && error.code === 'ENOTFOUND') return callback(null); // not setup if (error) return callback(error); @@ -190,9 +191,9 @@ function getEmailDnsRecords(callback) { var i; for (i = 0; i < txtRecords.length; i++) { - if (txtRecords[i].join(' ').indexOf('v=spf1 ') !== 0) continue; // not SPF - records.spf.value = txtRecords[i].join(' '); - records.spf.status = records.spf.value.indexOf(' a:' + config.adminFqdn() + ' ') !== -1; + if (txtRecords[i].join('').indexOf('v=spf1 ') !== 0) continue; // not SPF + records.spf.value = txtRecords[i].join(''); + records.spf.status = records.spf.value.indexOf(' a:' + config.adminFqdn()) !== -1; break; }