const express = require('express') const dns = require('dns'); const swipl = require('swipl'); const app = express() const PORT = 3000 swipl.call('assert(ip("_", "_"))'); swipl.call('assert(nip("_", "_"))'); swipl.call('assert(all("_", "_"))'); swipl.call('assert((allowed_to_send_email(Domain, IP) :- (ip(Domain, IP)), once(\\+ all(Domain, "-"); \\+ nip(Domain, IP))))'); const spf = (req, res) => { try { const { domain } = req.query; console.log(domain) let spf = "" dns.resolveTxt(domain, (err, address) => { if (err) { console.error(err) return res.status(400).send("ERROR: occur on resolving SPF") } else { console.log(address) for (let i = 0; i < address.length; i++) { const text = address[i][0]; console.log(text) if (text.indexOf("v=spf") !== -1) { spf = text; break; } } } if (!spf) { return res.status(400).send(`ERROR: No SPF record found in ${domain}`) } const spfArray = spf.split(" "); console.log(spfArray) return parseSPF(domain, spfArray, swipl, domain, (err) => { if (err) { console.error(err) return res.status(err.code).send(err.message) } const query = new swipl.Query(`allowed_to_send_email(${domain}, IP)`); let ret = null; let result = [] while (ret = query.next()) { console.log(`Allowed IP value is: ${ret.IP}`); result.push(`${ret.IP}`); } query.close(); return res.status(200).send(result) }) }) } catch (e) { console.log(e) } } const parseSPF = (domain, spf, swipl, curr, callback) => { console.log(domain, spf, curr) let err = undefined if (spf.length === 0) { return callback(err); } const str = spf[0] //ip46 if (str.match("^[+~?]?ip[46]:(.+)$")) { console.log("matched ip[46]!") swipl.call(`assert(ip(${domain}, "${str.match("^[+~?]?ip[46]:(.+)")[1]}"))`); console.log(`assert(ip(${domain}, "${str.match("^[+~?]?ip[46]:(.+)")[1]}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } else if (str.match("^-ip4:(.+)$")) { console.log("matched -ip[46]!") swipl.call(`assert(nip(${domain}, "${str.match("^-ip[46]:(.+)")[1]}"))`); console.log(`assert(nip(${domain}, "${str.match("^-ip[46]:(.+)")[1]}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } //a else if (str.match("^[+~?]?a$")) { console.log("matched a!") return dns.lookup(domain, (err, address, family) => { if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving a" } return callback(err); } swipl.call(`assert(ip(${domain}, "${address}"))`); console.log(`assert(ip(${domain}, "${address}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); }); } else if (str.match("^-a$")) { console.log("matched -a!") return dns.lookup(domain, (err, address, family) => { if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving a" } return callback(err); } swipl.call(`assert(nip(${domain}, "${address}"))`); console.log(`assert(nip(${domain}, "${address}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); }); } //mx else if (str.match("^[+~?]?mx$")) { console.log("matched mx!") return dns.resolveMx(domain, (err, address) => { for (let i = 0; i < address.length; i++) { const mx = address[i].exchange; dns.lookup(mx, (err, address, family) => { if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving mx" } return callback(err); } swipl.call(`assert(ip(${domain}, "${address}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); }); } }) } else if (str.match("^-mx$")) { console.log("matched -mx!") return dns.resolveMx(domain, (err, address) => { for (let i = 0; i < address.length; i++) { const mx = address[i].exchange; dns.lookup(mx, (err, address, family) => { if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving mx" } return callback(err); } swipl.call(`assert(nip(${domain}, "${address}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); }); } }) } //include else if (str.match("^include:(.+)$")) { next = str.match("^include:(.+)$")[1] console.log("matched include:", next) let txt = "" dns.resolveTxt(next, (err, address) => { console.log("hello") if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving SPF" } return callback(err); } else { console.log(address) for (let i = 0; i < address.length; i++) { const text = address[i][0]; console.log(text) if (text.indexOf("v=spf") !== -1) { txt = text; break; } } } if (!txt) { err = { code: 400, message: "ERROR: Fail to find SPF record" } return callback(err); } const spfArray = txt.split(" "); console.log(spfArray) return parseSPF(domain, spfArray, swipl, next, (err) => { if (err) { console.error(err) return res.status(err.code).send(err.message) } return parseSPF(domain, spf.slice(1), swipl, curr, callback); }) }) } //redirect else if (str.match("^redirect=(.+)$")) { next = str.match("^redirect=(.+)$")[1] console.log("matched redirect:", next) let txt = "" dns.resolveTxt(next, (err, address) => { if (err) { console.error(err) err = { code: 400, message: "ERROR: occur on resolving SPF" } return callback(err); } else { console.log(address) for (let i = 0; i < address.length; i++) { const text = address[i][0]; console.log(text) if (text.indexOf("v=spf") !== -1) { txt = text; break; } } } if (!txt) { err = { code: 400, message: "ERROR: Fail to find SPF record" } return callback(err); } const spfArray = txt.split(" "); console.log(spfArray) return parseSPF(domain, spfArray, swipl, domain, (err) => { if (err) { console.error(err) return res.status(err.code).send(err.message) } else { return parseSPF(domain, spf.slice(1), swipl, curr, callback); } }) }) } //all else if (str.match("^-all$")) { console.log("matched -all!") swipl.call(`assert(all(${domain}, "-"))`); console.log(`assert(all(${domain}, "-"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } //others else { return parseSPF(domain, spf.slice(1), swipl, curr, callback); } } app.get('/spf', spf) app.listen(PORT, (err) => { if (err) { return console.error(err); } return console.log(`Server is listening on ${PORT}`); }); //test // const spfArray = "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all".split(" "); // const domain = "gmail.com" // parseSPF(domain, spfArray, swipl, domain, (err) => { // if (err) { // return res.status(err.code).send(err.message) // } // const query = new swipl.Query(`allowed_to_send_email(${domain}, IP)`); // let ret = null; // let result = [] // while (ret = query.next()) { // console.log(`Allowed IP value is: ${ret.IP}`); // result.push(`${ret.IP}`); // } // query.close(); // // return res.status(200).send(result) // });