var dns = require('dns').promises;
var net = require('net');

function isPrivateIp(ip) {
  if (!ip) return true;
  if (net.isIPv4(ip)) {
    var p = ip.split('.').map(Number);
    if (p[0] === 10 || p[0] === 127 || p[0] === 0) return true;
    if (p[0] === 100 && p[1] >= 64 && p[1] <= 127) return true;
    if (p[0] === 169 && p[1] === 254) return true;
    if (p[0] === 172 && p[1] >= 16 && p[1] <= 31) return true;
    if (p[0] === 192 && p[1] === 168) return true;
    if (p[0] >= 224) return true;
    return false;
  }
  if (net.isIPv6(ip)) {
    var low = ip.toLowerCase();
    if (low === '::1' || low === '::') return true;
    if (low.startsWith('fe80:') || low.startsWith('fc') || low.startsWith('fd')) return true;
    return false;
  }
  return true;
}

async function assertSafeHttpsUrl(urlStr, label) {
  var u;
  try { u = new URL(urlStr); }
  catch (e) { throw new Error((label || 'URL') + ' is invalid'); }
  if (u.protocol !== 'https:') throw new Error((label || 'URL') + ' must use https://');
  if (u.username || u.password) throw new Error((label || 'URL') + ' must not include credentials');
  var addrs = await dns.lookup(u.hostname, { all: true });
  if (!addrs.length) throw new Error((label || 'URL') + ' did not resolve');
  for (var i = 0; i < addrs.length; i++) {
    if (isPrivateIp(addrs[i].address)) throw new Error((label || 'URL') + ' resolves to a private IP');
  }
  return u;
}

module.exports = {
  assertSafeHttpsUrl: assertSafeHttpsUrl,
  isPrivateIp: isPrivateIp
};
