25 lines
1.7 KiB
JavaScript
25 lines
1.7 KiB
JavaScript
'use strict';
|
|
const {isIP}=require('node:net');
|
|
const dns=require('node:dns').promises;
|
|
// Resolve once in the broker and bind the approved numeric address to the dial.
|
|
// Active IP policy uses this approved tuple for numeric proxy CONNECT; ordinary
|
|
// hostname CONNECT remains supported when that origin IP policy is inactive.
|
|
function createDnsPolicyRoute({lookup=dns.lookup,blocked,current}={}){
|
|
if(typeof lookup!=='function'||typeof blocked!=='function'||typeof current!=='function')throw Error('broker-route-denied');
|
|
return async({hostname,port,signal})=>{
|
|
const check=()=>{if(signal?.aborted||current()!==true)throw Error('broker-route-denied');};
|
|
try{
|
|
check();if(typeof hostname!=='string'||!hostname||hostname.length>253||!Number.isSafeInteger(port)||port<1||port>65535)throw Error();
|
|
const literal=hostname.replace(/^\[|\]$/g,'');
|
|
if(isIP(literal)){if(blocked(literal))throw Error();check();return Object.freeze({approved:true,address:literal,port});}
|
|
const records=await lookup(hostname,{all:true,verbatim:true});check();
|
|
if(!Array.isArray(records)||!records.length||records.length>64||records.some(r=>!r||typeof r.address!=='string'||!isIP(r.address)||blocked(r.address)))throw Error();
|
|
const address=records[0].address;check();return Object.freeze({approved:true,address,port});
|
|
}catch{throw Error('broker-route-denied');}
|
|
};
|
|
}
|
|
function assertSupportedDns(policy,network,{numericOriginSupported=false}={}){
|
|
if(policy.enterprise?.dns?.mode==='secure'||!numericOriginSupported&&network.mode==='fixed_servers'&&policy.security?.dns?.enabled!==false&&policy.security?.dns?.blockedIPs?.length)throw Error('broker-route-unsupported');
|
|
}
|
|
module.exports={createDnsPolicyRoute,assertSupportedDns};
|