const express = require('express') const dns = require('dns'); const swipl = require('swipl'); const path = require('path'); const app = express() const PORT = 3000 const spf = (req, res) => { try { res.setHeader('Content-Type', 'application/json'); const { domain } = req.query; console.log(domain) let spf = "" dns.resolveTxt(domain, (err, address) => { if (err) { console.error("ERROR", err) return res.status(400).json("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) { console.error("ERROR: not spf") return res.status(400).json(`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("ERROR", err) return res.status(err.code).json(err.message) } else { swipl.call('assert(ip4("_", "_"))'); swipl.call('assert(nip4("_", "_"))'); swipl.call('assert(ip6("_", "_"))'); swipl.call('assert(nip6("_", "_"))'); swipl.call('assert(all("_", "_"))'); swipl.call('assert((allowed_to_send_email4(Domain, IP) :- (ip4(Domain, IP)), once(\\+ all(Domain, "-"); \\+ nip4(Domain, IP))))'); swipl.call('assert((allowed_to_send_email6(Domain, IP) :- (ip6(Domain, IP)), once(\\+ all(Domain, "-"); \\+ nip6(Domain, IP))))'); let query = new swipl.Query(`allowed_to_send_email4(${domain}, IP)`); let ret = null; let allowedIP4 = [] while (ret = query.next()) { console.log(`Allowed IP value is: ${ret.IP}`); allowedIP4.push(`${ret.IP}`); } query.close(); query = new swipl.Query(`allowed_to_send_email6(${domain}, IP)`); ret = null; let allowedIP6 = [] while (ret = query.next()) { console.log(`Allowed IP value is: ${ret.IP}`); allowedIP6.push(`${ret.IP}`); } query.close(); swipl.cleanup() swipl.initialise() return res.status(200).json({spf: spf, allowedIP4: [...new Set(allowedIP4)], allowedIP6: [...new Set(allowedIP6)]}) } }) }) } catch (e) { console.log(e) } } const a = (req, res) => { try { res.setHeader('Content-Type', 'application/json'); const { domain } = req.query; console.log(domain) dns.resolve4(domain, (err, address) => { if (err) { console.log(err) return res.status(400).json("ERROR: No a record found") } else { return res.status(200).json(address) } }) } catch (err) { console.log(err) } } const mx = (req, res) => { try { res.setHeader('Content-Type', 'application/json'); const { domain } = req.query; console.log(domain) dns.resolveMx(domain, (err, address) => { if (err) { console.log(err) return res.status(400).json("ERROR: No mx record found") } else { return res.status(200).json(address) } }) } catch (err) { console.log(err) } } const parseSPF = (domain, spf, swipl, curr, callback) => { try { console.log(domain, spf, curr) let err = undefined if (spf.length === 0) { return callback(err); } const str = spf[0] //ip4 if (str.match("^[+~?]?ip4:(.+)$")) { console.log("matched ip4!") swipl.call(`assert(ip4(${domain}, "${str.match("^[+~?]?ip4:(.+)")[1]}"))`); console.log(`assert(ip4(${domain}, "${str.match("^[+~?]?ip4:(.+)")[1]}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } else if (str.match("^-ip4:(.+)$")) { console.log("matched -ip4!") swipl.call(`assert(nip4(${domain}, "${str.match("^-ip4:(.+)")[1]}"))`); console.log(`assert(nip4(${domain}, "${str.match("^-ip4:(.+)")[1]}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } //ip6 if (str.match("^[+~?]?ip6:(.+)$")) { console.log("matched ip6!") swipl.call(`assert(ip6(${domain}, "${str.match("^[+~?]?ip6:(.+)")[1]}"))`); console.log(`assert(ip6(${domain}, "${str.match("^[+~?]?ip6:(.+)")[1]}"))`); return parseSPF(domain, spf.slice(1), swipl, curr, callback); } else if (str.match("^-ip6:(.+)$")) { console.log("matched -ip6!") swipl.call(`assert(nip6(${domain}, "${str.match("^-ip6:(.+)")[1]}"))`); console.log(`assert(nip6(${domain}, "${str.match("^-ip6:(.+)")[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("ERROR", err) err = { code: 400, message: "ERROR: occur on resolving a" } return callback(err); } else { swipl.call(`assert(ip4(${domain}, "${address}"))`); console.log(`assert(ip4(${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("ERROR", err) err = { code: 400, message: "ERROR: occur on resolving a" } return callback(err); } else { swipl.call(`assert(nip4(${domain}, "${address}"))`); console.log(`assert(nip4(${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("ERROR", err) err = { code: 400, message: "ERROR: occur on resolving mx" } return callback(err); } else { swipl.call(`assert(ip4(${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("ERROR", err) err = { code: 400, message: "ERROR: occur on resolving mx" } return callback(err); } else { swipl.call(`assert(nip4(${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("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); } else { const spfArray = txt.split(" "); console.log(spfArray) return parseSPF(domain, spfArray, swipl, next, (err) => { if (err) { console.error("ERROR", err) err = { code: 400, message: "ERROR: occur on redirect" } return callback(err); } else { 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("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("ERROR", err) err = { code: 400, message: "ERROR: occur on redirect" } return callback(err); } 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); } } catch (e) { console.log(e) } } app.get('/spf', spf) app.get('/a', a) app.get('/mx', mx) app.use(express.static(path.join(__dirname, 'public'))); app.listen(PORT, (err) => { if (err) { return console.error("ERROR", err); } else { return console.log(`Server is listening on ${PORT}`); } }); //test // const spfArray = "v=spf1 include:spf1.baidu.com include:spf2.baidu.com include:spf3.baidu.com include:spf4.baidu.com mx ptr -all".split(" "); // const domain = "baidu.com" // parseSPF(domain, spfArray, swipl, domain, (err) => { // if (err) { // return res.status(err.code).send(err.message) // } // else { // swipl.call('assert(ip4("_", "_"))'); // swipl.call('assert(nip4("_", "_"))'); // swipl.call('assert(all("_", "_"))'); // swipl.call('assert((allowed_to_send_email(Domain, IP) :- (ip(Domain, IP)), once(\\+ all(Domain, "-"); \\+ nip(Domain, IP))))'); // 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(); // swipl.cleanup() // swipl.initialise() // } // });