4

Is there any way to run an nslookup / dig in Google App Script? I m looking to get the results of this command in a Google Spreadsheet.

Thanks

Yannis
  • 6,047
  • 5
  • 43
  • 62

8 Answers8

4

Find a web service that will let you use a GET or POST anonymously to query DNS info using a RESTful API, and you'll be able to use UrlFetchApp.fetch() to access it.

For example, StatDNS has a simple API. Here's a custom function that will resolve a Domain Name to IPv4 Address.

screenshot

code

/**
 * Peform a Network Service Lookup, using StatDNS API.
 *
 * @param {"google.com"} dn    A well-formed domain name to resolve.
 * @return {String}            Resolved IP address
 * @customfunction
 */
function NSLookup(dn) {
  var url = "http://api.statdns.com/%FQDN%/a".replace("%FQDN%",dn);
  var result = UrlFetchApp.fetch(url,{muteHttpExceptions:true});
  var rc = result.getResponseCode();
  var response = JSON.parse(result.getContentText());
  if (rc !== 200) {
    throw new Error( response.message );
  }
  var ip = response.answer[0].rdata;
  return ip;
}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
3

Since Mogsdad's suggested service is no longer free, I've made a modification of his code to fetch any DNS register from https://hackertarget.com/dns-lookup/ with a custom function of Apps Script.

code

/**
 * Peform a Network Service Lookup, using hackertarget API.
 *
 * @param {"google.com"} dn    A well-formed domain name to resolve.
 * @param {"A"} type           Type of data to be returned, such as A, AAA, MX, NS...
 * @return {String}            Resolved IP address
 * @customfunction
 */
function NSLookup(dn,type) {
  var url = "http://api.hackertarget.com/dnslookup/?q=" + dn;
  var result = UrlFetchApp.fetch(url,{muteHttpExceptions:true});
  var rc = result.getResponseCode();
  var response = result.getContentText();
  if (rc !== 200) {
    throw new Error( response.message );
  }
  //var resp = response.answer[0].rdata;
  var registers = response.split("\n");
  var register;
  var arrayLength;  // Not all queries return the same number of items
  var result = "";
  for (var i in registers) {
    register = registers[i].split("\t");
    arrayLength = register.length;
    if (register[arrayLength - 2] == type) {
      result = register[arrayLength - 1];
      break;
    }
  }
  return result;
}
  • Often our error messages are not very descriptive, so i added the error code to the message: throw new Error( rc + ": " + response.message ); – fotoflo Dec 15 '19 at 15:03
3

Google Public DNS Provides API https://dns.google.com/resolve? which can be used in Apps Script program with UrlFetchApp to fetch the results from Google Public DNS Server.

Here is a sample code.

function NSLookup() {
  var api_url = 'https://dns.google.com/resolve'; // Google Pubic DNS API Url
  var type = 'MX'; // Type of record to fetch, A, AAAA, MX, CNAME, TXT, ANY
  var name = 'accemy.com'; // The domain name to lookup
  var requestUrl = api_url + '?name=' + name + '&type=' + type; // Build request URL
  var response = UrlFetchApp.fetch(requestUrl); // Fetch the reponse from API
  var responseText = response.getContentText(); // Get the response text

  var json = JSON.parse(responseText); // Parse the JSON text

  var answers = json.Answer.map(function(ans) {
    return ans.data
  }).join('\n'); // Get the values
  return answers;
}
Waqar Ahmad
  • 3,716
  • 1
  • 16
  • 27
3

Here is the code that I use in my Google Sheets. It works as of today. It also throws proper error messages.

function NSLookup(type, domain) {
   
  if (typeof type == 'undefined') {
    throw new Error('Missing parameter 1 dns type');
  }
   
  if (typeof domain == 'undefined') {
    throw new Error('Missing parameter 2 domain name');
  }
  
  type = type.toUpperCase();
   
  var url = 'https://cloudflare-dns.com/dns-query?name=' + encodeURIComponent(domain) + '&type=' + encodeURIComponent(type);
  
  var options = {
    "muteHttpExceptions": true,
    "headers": {
      "accept": "application/dns-json"
    }
  };
  
  var result = UrlFetchApp.fetch(url, options);
  var rc = result.getResponseCode();
  var resultText = result.getContentText();
   
  if (rc !== 200) {
    throw new Error(rc);
  }
  
  var errors = [
    { "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
  ];
  
  var response = JSON.parse(resultText);
   
  if (response.Status !== 0) {
    return errors[response.Status].name;
  }
   
  var outputData = [];
   
  for (var i in response.Answer) {
    outputData.push(response.Answer[i].data);
  }
   
  var outputString = outputData.join(',');
   
  return outputString;
   
}

Here is a sample output in Google sheets:

screenshot

0

I have run into some problems using both of these reliably, especially for MX Records. I have written a simple backend service to resolve names using NodeJS.

Node JS Service

const http = require('http');
const dns = require('dns');
const url = require('url');
const port = 80;

function reqHandle(req, res) {
  const path = url.parse(req.url, true);
  const type = path.pathname.substring(1);
  if(['A', 'MX', 'NS'].indexOf(type) === -1 || !path.query.q) {
    res.writeHead(500);
    res.write('Unable to determine request type');
    res.end();
    return
  }
  console.log(`Lookup ${type} for ${path.query.q}`);
  dns.resolve(path.query.q, type, (err, v) => {
    res.writeHead(200, {"Content-Type": "application/json"});
    res.write(JSON.stringify({result: v || []}, null, 2));
    res.end();
  });
}

const server = http.createServer(reqHandle);

server.listen(port, '127.0.0.1', (err) => {
  if(err) {
    console.warn("Unable to start server", err);
    return;
  }
  console.log(`Server Listening on port ${port}`);
});

and use the following code in the script editor in Google Sheets (Tools -> Script Editor). You will need to edit the function to point to your Server address.

/**
 * Peform a Network Service Lookup, using custom API.
 *
 * @param {"google.com"} dn    A well-formed domain name to resolve.
 * @param {"A"} type           Type of data to be returned, such as A, AAA, MX, NS...
 * @return {String}            Resolved IP address
 * @customfunction
 */
function NSLookup(dn,type) {
  var url = "http://my-server-address.com/"+type+"?q=" + dn;
  var result = UrlFetchApp.fetch(url,{muteHttpExceptions:true});
  var rc = result.getResponseCode();
  var response = result.getContentText();
  if (rc !== 200) {
    throw new Error( response.message );
  }
  var data = JSON.parse(response);
  switch(type) {
    case "A":
    case "NS":
      return data.result.join(' ');
    case "MX":
      return data.result.map(function(v) {return v.exchange}).join(' ');
  }
    throw new Error( "Unsupported query type" );
}
Chris Seufert
  • 819
  • 1
  • 7
  • 17
0

I have come up with an alternative solution since Cloudflare starting offering public DNS over HTTPS.

Add this app script to your google sheets, and use NSLookup("goole.com", "A",)

/**
 * Peform a Network Service Lookup, using CloudFlare Public DNS-over-HTTPS API.
 *
 * @param {"google.com"} dn    A well-formed domain name to resolve.
 * @param {"A"} type           Type of data to be returned, such as A, AAA, MX, NS...
 * @param {true} first         Return first result only, else return all 
 * @return {String}            DNS Lookup Result
 * @customfunction
 */
function NSLookup(dn,type, first) {
  var url = "https://cloudflare-dns.com/dns-query?ct=application/dns-json&name=" + dn + "&type=" + type;
  var result = UrlFetchApp.fetch(url,{muteHttpExceptions:true});
  var rc = result.getResponseCode();
  var response = result.getContentText();
  if (rc !== 200) {
    throw new Error( response.message );
  }
  var resultData = JSON.parse(response);
  var result = [];
  for(var i = 0; i < resultData.Answer.length; i++) {
    result.push(resultData.Answer[i].data);
  }
  if(first) {
    return result.length > 0 ? result[0] : "";
  }
  return result.join(" ");
}
Chris Seufert
  • 819
  • 1
  • 7
  • 17
0

This is the updated function code for the script that worked for me, based on @Mogsdad code but including also a record type option defaulted to A and updated URL.

/**
 * Peform a Network Service Lookup, using google.
 *
 * @param {1} name    A well-formed domain name to resolve.
 * @param {2} type           Type of data to be returned, such as A, AAA, MX, NS...
 * @return {String}            DNS Lookup Result
 * @customfunction
 */


function NSLookup (name='test.com',type='A') {
  var api_url = 'https://dns.google/resolve'; // Google Pubic DNS API Url
  var requestUrl = api_url + '?name=' + name + '&type=' + type; // Build request URL
  var response = UrlFetchApp.fetch(requestUrl); // Fetch the reponse from API
  var responseText = response.getContentText(); // Get the response text

  var json = JSON.parse(responseText); // Parse the JSON text

  var answers = json.Answer.map(function (ans) {
    return ans.data
  }).join('\n'); // Get the values
  return answers;
}
luison
  • 1,850
  • 1
  • 19
  • 33
0

    function NSLookup(dn) {
  var api_url = 'https://dns.google.com/resolve'; // Google Pubic DNS API Url
  var type = CNAME; // Type of record to fetch, A, AAAA, MX, CNAME, TXT, ANY
  var name = dn; // The domain name to lookup
  var requestUrl = api_url + '?name=' + name + '&type=' + type; // Build request URL
  var response = UrlFetchApp.fetch(requestUrl); // Fetch the reponse from API
  var responseText = response.getContentText(); // Get the response text

  var json = JSON.parse(responseText); // Parse the JSON text

  var answers = json.Answer.map(function(ans) {
    return ans.data
  }).join('\n'); // Get the values
  return answers;
}
user460111
  • 13
  • 1
  • 4