function NSLookup(type, domain, useCache = false, minCacheTTL = 30) {
if (typeof type == 'undefined') {
throw new Error('Missing parameter 1 dns type');
if (typeof domain == 'undefined') {
throw new Error('Missing parameter 2 domain name');
if (typeof useCache != "boolean") {
throw new Error('Only boolean values allowed in 3 use cache');
if (typeof minCacheTTL != "number") {
throw new Error('Only numeric values allowed in 4 min cache ttl');
type = type.toUpperCase();
domain = domain.toLowerCase();
cacheKey = domain + "@" + type;
cacheHash = Utilities.base64Encode(cacheKey);
cacheBinKey = "nslookup-result-" + cacheHash;
cache = CacheService.getScriptCache();
const cachedResult = cache.get(cacheBinKey);
if (cachedResult != null) {
const url = 'https://cloudflare-dns.com/dns-query?name=' + encodeURIComponent(domain) + '&type=' + encodeURIComponent(type);
muteHttpExceptions: true,
accept: "application/dns-json"
const result = UrlFetchApp.fetch(url, options);
const rc = result.getResponseCode();
const resultText = result.getContentText();
{ name: "NoError", description: "No Error"}, // 0
{ name: "FormErr", description: "Format Error"}, // 1
{ name: "ServFail", description: "Server Failure"}, // 2
{ name: "NXDomain", description: "Non-Existent Domain"}, // 3
{ name: "NotImp", description: "Not Implemented"}, // 4
{ name: "Refused", description: "Query Refused"}, // 5
{ name: "YXDomain", description: "Name Exists when it should not"}, // 6
{ name: "YXRRSet", description: "RR Set Exists when it should not"}, // 7
{ name: "NXRRSet", description: "RR Set that should exist does not"}, // 8
{ name: "NotAuth", description: "Not Authorized"} // 9
const response = JSON.parse(resultText);
if (response.Status !== 0) {
return errors[response.Status].name;
for (const i in response.Answer) {
outputData.push(response.Answer[i].data);
const ttl = response.Answer[i].TTL;
cacheTTL = Math.min(cacheTTL || ttl, ttl);
const outputString = outputData.join(',');
cache.put(cacheBinKey, outputString, Math.max(cacheTTL, minCacheTTL));