Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

haraka-net-utils

Package Overview
Dependencies
Maintainers
4
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

haraka-net-utils - npm Package Compare versions

Comparing version 1.5.4 to 1.6.0

CHANGELOG.md

520

index.js

@@ -1,98 +0,100 @@

'use strict';
'use strict'
// node.js built-ins
const {Resolver} = require('dns').promises;
const dns = new Resolver({timeout: 25000, tries: 1});
const net = require('net');
const os = require('os');
const punycode = require('punycode/')
const { Resolver } = require('dns').promises
const dns = new Resolver({ timeout: 25000, tries: 1 })
const net = require('net')
const os = require('os')
const punycode = require('punycode.js')
// npm modules
const ipaddr = require('ipaddr.js');
const sprintf = require('sprintf-js').sprintf;
const tlds = require('haraka-tld');
const ipaddr = require('ipaddr.js')
const sprintf = require('sprintf-js').sprintf
const tlds = require('haraka-tld')
const locallyBoundIPs = [];
const locallyBoundIPs = []
// export config, so config base path can be overloaded by tests
exports.config = require('haraka-config');
exports.config = require('haraka-config')
exports.long_to_ip = function (n) {
let d = n%256;
for (let i=3; i>0; i--) {
n = Math.floor(n/256);
d = `${n%256}.${d}`;
let d = n % 256
for (let i = 3; i > 0; i--) {
n = Math.floor(n / 256)
d = `${n % 256}.${d}`
}
return d;
return d
}
exports.dec_to_hex = function (d) {
return d.toString(16);
return d.toString(16)
}
exports.hex_to_dec = function (h) {
return parseInt(h, 16);
return parseInt(h, 16)
}
exports.ip_to_long = function (ip) {
if (!net.isIPv4(ip)) { return false; }
if (!net.isIPv4(ip)) return false
const d = ip.split('.');
return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
const d = ip.split('.')
return ((+d[0] * 256 + +d[1]) * 256 + +d[2]) * 256 + +d[3]
}
exports.octets_in_string = function (str, oct1, oct2) {
let oct1_idx;
let oct2_idx;
let oct1_idx
let oct2_idx
// test the largest of the two octets first
if (oct2.length >= oct1.length) {
oct2_idx = str.lastIndexOf(oct2);
if (oct2_idx === -1) return false;
oct2_idx = str.lastIndexOf(oct2)
if (oct2_idx === -1) return false
oct1_idx = (str.substring(0, oct2_idx) +
str.substring(oct2_idx + oct2.length)).lastIndexOf(oct1);
if (oct1_idx === -1) return false;
oct1_idx = (
str.substring(0, oct2_idx) + str.substring(oct2_idx + oct2.length)
).lastIndexOf(oct1)
if (oct1_idx === -1) return false
return true; // both were found
return true // both were found
}
oct1_idx = str.indexOf(oct1);
if (oct1_idx === -1) return false;
oct1_idx = str.indexOf(oct1)
if (oct1_idx === -1) return false
oct2_idx = (str.substring(0, oct1_idx) +
str.substring(oct1_idx + oct1.length)).lastIndexOf(oct2);
if (oct2_idx === -1) return false;
oct2_idx = (
str.substring(0, oct1_idx) + str.substring(oct1_idx + oct1.length)
).lastIndexOf(oct2)
if (oct2_idx === -1) return false
return true;
return true
}
exports.is_ip_in_str = function (ip, str) {
if (!str) return false;
if (!ip) return false;
if (!net.isIPv4(ip)) return false; // IPv4 only, for now
if (!str) return false
if (!ip) return false
if (!net.isIPv4(ip)) return false // IPv4 only, for now
const host_part = (tlds.split_hostname(str,1))[0].toString();
const octets = ip.split('.');
const host_part = tlds.split_hostname(str, 1)[0].toString()
const octets = ip.split('.')
// See if the 3rd and 4th octets appear in the string
if (this.octets_in_string(host_part, octets[2], octets[3])) {
return true;
return true
}
// then the 1st and 2nd octets
if (this.octets_in_string(host_part, octets[0], octets[1])) {
return true;
return true
}
// Whole IP in hex
let host_part_copy = host_part;
const ip_hex = this.dec_to_hex(this.ip_to_long(ip));
for (let i=0; i<4; i++) {
const part = host_part_copy.indexOf(ip_hex.substring(i*2, (i*2)+2));
if (part === -1) break;
if (i === 3) return true;
host_part_copy = host_part_copy.substring(0, part) +
host_part_copy.substring(part+2);
let host_part_copy = host_part
const ip_hex = this.dec_to_hex(this.ip_to_long(ip))
for (let i = 0; i < 4; i++) {
const part = host_part_copy.indexOf(ip_hex.substring(i * 2, i * 2 + 2))
if (part === -1) break
if (i === 3) return true
host_part_copy =
host_part_copy.substring(0, part) + host_part_copy.substring(part + 2)
}
return false;
return false
}

@@ -104,34 +106,32 @@

private10: /^10\./, // 10/8
private192: /^192\.168\./, // 192.168/16
private10: /^10\./, // 10/8
private192: /^192\.168\./, // 192.168/16
// 172.16/16 .. 172.31/16
private172: /^172\.(1[6-9]|2[0-9]|3[01])\./, // 172.16/12
private172: /^172\.(1[6-9]|2[0-9]|3[01])\./, // 172.16/12
// RFC 5735
testnet1: /^192\.0\.2\./, // 192.0.2.0/24
testnet1: /^192\.0\.2\./, // 192.0.2.0/24
testnet2: /^198\.51\.100\./, // 198.51.100.0/24
testnet3: /^203\.0\.113\./, // 203.0.113.0/24
testnet3: /^203\.0\.113\./, // 203.0.113.0/24
}
exports.is_private_ipv4 = function (ip) {
// RFC 1918, reserved as "private" IP space
if (re_ipv4.private10.test(ip)) return true;
if (re_ipv4.private192.test(ip)) return true;
if (re_ipv4.private172.test(ip)) return true;
if (re_ipv4.private10.test(ip)) return true
if (re_ipv4.private192.test(ip)) return true
if (re_ipv4.private172.test(ip)) return true
if (re_ipv4.testnet1.test(ip)) return true;
if (re_ipv4.testnet2.test(ip)) return true;
if (re_ipv4.testnet3.test(ip)) return true;
if (re_ipv4.testnet1.test(ip)) return true
if (re_ipv4.testnet2.test(ip)) return true
if (re_ipv4.testnet3.test(ip)) return true
return false;
return false
}
exports.on_local_interface = function (ip) {
if (locallyBoundIPs.length === 0) {
const ifList = os.networkInterfaces();
const ifList = os.networkInterfaces()
for (const ifName of Object.keys(ifList)) {
for (const addr of ifList[ifName]) {
locallyBoundIPs.push(addr.address);
locallyBoundIPs.push(addr.address)
}

@@ -141,11 +141,10 @@ }

return locallyBoundIPs.includes(ip);
return locallyBoundIPs.includes(ip)
}
exports.is_local_host = async function (dst_host) {
// Is the destination hostname/IP delivered to a hostname or IP
// that's local to _this_ mail server?
const local_ips = [];
const dest_ips = [];
const local_ips = []
const dest_ips = []

@@ -157,13 +156,13 @@ try {

const local_hostname = this.get_primary_host_name()
local_ips.push(...await this.get_ips_by_host(local_hostname));
local_ips.push(...(await this.get_ips_by_host(local_hostname)))
if (net.isIP(dst_host)) { // an IP address
if (net.isIP(dst_host)) {
// an IP address
dest_ips.push(dst_host)
}
else { // a hostname
} else {
// a hostname
if (dst_host === local_hostname) return true
dest_ips.push(...await this.get_ips_by_host(dst_host));
dest_ips.push(...(await this.get_ips_by_host(dst_host)))
}
}
catch (e) {
} catch (e) {
// console.error(e)

@@ -181,27 +180,26 @@ return false

exports.is_local_ip = function (ip) {
if (this.on_local_interface(ip)) return true
if (this.on_local_interface(ip)) return true;
if (net.isIPv4(ip)) return this.is_local_ipv4(ip)
if (net.isIPv6(ip)) return this.is_local_ipv6(ip)
if (net.isIPv4(ip)) return this.is_local_ipv4(ip);
if (net.isIPv6(ip)) return this.is_local_ipv6(ip);
// console.error(`invalid IP address: ${ip}`);
return false;
return false
}
exports.is_local_ipv4 = function (ip) {
if ('0.0.0.0' === ip) return true; // RFC 5735
if ('0.0.0.0' === ip) return true // RFC 5735
// 127/8 (loopback) # RFC 1122
if (re_ipv4.loopback.test(ip)) return true;
if (re_ipv4.loopback.test(ip)) return true
// link local: 169.254/16 RFC 3927
if (re_ipv4.link_local.test(ip)) return true;
if (re_ipv4.link_local.test(ip)) return true
return false;
return false
}
const re_ipv6 = {
loopback: /^(0{1,4}:){7}0{0,3}1$/,
link_local: /^fe80::/i,
loopback: /^(0{1,4}:){7}0{0,3}1$/,
link_local: /^fe80::/i,
unique_local: /^f(c|d)[a-f0-9]{2}:/i,

@@ -211,33 +209,35 @@ }

exports.is_local_ipv6 = function (ip) {
if (ip === '::') return true; // RFC 5735
if (ip === '::1') return true; // RFC 4291
if (ip === '::') return true // RFC 5735
if (ip === '::1') return true // RFC 4291
// 2 more IPv6 notations for ::1
// 0:0:0:0:0:0:0:1 or 0000:0000:0000:0000:0000:0000:0000:0001
if (re_ipv6.loopback.test(ip)) return true;
if (re_ipv6.loopback.test(ip)) return true
// link local: fe80::/10, RFC 4862
if (re_ipv6.link_local.test(ip)) return true;
if (re_ipv6.link_local.test(ip)) return true
// unique local (fc00::/7) -> fc00: - fd00:
if (re_ipv6.unique_local.test(ip)) return true;
if (re_ipv6.unique_local.test(ip)) return true
return false;
return false
}
exports.is_private_ip = function (ip) {
if (net.isIPv4(ip)) return this.is_local_ipv4(ip) || this.is_private_ipv4(ip);
if (net.isIPv6(ip)) return this.is_local_ipv6(ip);
return false;
if (net.isIPv4(ip)) return this.is_local_ipv4(ip) || this.is_private_ipv4(ip)
if (net.isIPv6(ip)) return this.is_local_ipv6(ip)
return false
}
// backwards compatibility for non-public modules. Sunset: v3.0
exports.is_rfc1918 = exports.is_private_ip;
exports.is_rfc1918 = exports.is_private_ip
exports.is_ip_literal = function (host) {
return exports.get_ipany_re('^\\[(IPv6:)?','\\]$','').test(host) ? true : false;
return exports.get_ipany_re('^\\[(IPv6:)?', '\\]$', '').test(host)
? true
: false
}
exports.is_ipv4_literal = function (host) {
return /^\[(\d{1,3}\.){3}\d{1,3}\]$/.test(host) ? true : false;
return /^\[(\d{1,3}\.){3}\d{1,3}\]$/.test(host) ? true : false
}

@@ -247,32 +247,30 @@

if (!ipList || !ipList.length) {
console.error('same_ipv4_network, no ip list!');
return false;
console.error('same_ipv4_network, no ip list!')
return false
}
if (!net.isIPv4(ip)) {
console.error('same_ipv4_network, IP is not IPv4!');
return false;
console.error('same_ipv4_network, IP is not IPv4!')
return false
}
const first3 = ip.split('.').slice(0,3).join('.');
const first3 = ip.split('.').slice(0, 3).join('.')
for (let i=0; i < ipList.length; i++) {
for (let i = 0; i < ipList.length; i++) {
if (!net.isIPv4(ipList[i])) {
console.error('same_ipv4_network, IP in list is not IPv4!');
continue;
console.error('same_ipv4_network, IP in list is not IPv4!')
continue
}
if (first3 === ipList[i].split('.').slice(0,3).join('.'))
return true;
if (first3 === ipList[i].split('.').slice(0, 3).join('.')) return true
}
return false;
return false
}
exports.get_public_ip_async = async function () {
if (this.public_ip !== undefined) return this.public_ip // cache
if (this.public_ip !== undefined) return this.public_ip; // cache
// manual config override, for the cases where we can't figure it out
const smtpIni = exports.config.get('smtp.ini').main;
const smtpIni = exports.config.get('smtp.ini').main
if (smtpIni.public_ip) {
this.public_ip = smtpIni.public_ip;
return this.public_ip;
this.public_ip = smtpIni.public_ip
return this.public_ip
}

@@ -282,17 +280,16 @@

// should we hit a timeout or the module isn't installed.
this.public_ip = null;
this.public_ip = null
try {
this.stun = require('@msimerson/stun');
}
catch (e) {
e.install = 'Please install stun: "npm install -g stun"';
console.error(`${e.msg}\n${e.install}`);
this.stun = require('@msimerson/stun')
} catch (e) {
e.install = 'Please install stun: "npm install -g stun"'
console.error(`${e.msg}\n${e.install}`)
return
}
const timeout = 10;
const timeout = 10
const timer = setTimeout(() => {
return new Error('STUN timeout')
}, timeout * 1000);
}, timeout * 1000)

@@ -309,10 +306,10 @@ // Connect to STUN Server

const nu = this;
if (nu.public_ip !== undefined) return cb(null, nu.public_ip); // cache
const nu = this
if (nu.public_ip !== undefined) return cb(null, nu.public_ip) // cache
// manual config override, for the cases where we can't figure it out
const smtpIni = exports.config.get('smtp.ini').main;
const smtpIni = exports.config.get('smtp.ini').main
if (smtpIni.public_ip) {
nu.public_ip = smtpIni.public_ip;
return cb(null, nu.public_ip);
nu.public_ip = smtpIni.public_ip
return cb(null, nu.public_ip)
}

@@ -322,29 +319,28 @@

// should we hit a timeout or the module isn't installed.
nu.public_ip = null;
nu.public_ip = null
try {
nu.stun = require('@msimerson/stun');
nu.stun = require('@msimerson/stun')
} catch (e) {
e.install = 'Please install stun: "npm install -g stun"'
console.error(`${e.msg}\n${e.install}`)
return cb(e)
}
catch (e) {
e.install = 'Please install stun: "npm install -g stun"';
console.error(`${e.msg}\n${e.install}`);
return cb(e);
}
const timeout = 10;
const timeout = 10
const timer = setTimeout(() => {
return cb(new Error('STUN timeout'));
}, timeout * 1000);
return cb(new Error('STUN timeout'))
}, timeout * 1000)
// Connect to STUN Server
nu.stun.request(get_stun_server(), (error, res) => {
if (timer) clearTimeout(timer);
if (error) return cb(error);
if (timer) clearTimeout(timer)
if (error) return cb(error)
nu.public_ip = res.getXorAddress().address
cb(null, nu.public_ip);
cb(null, nu.public_ip)
})
}
function get_stun_server () {
function get_stun_server() {
// STUN servers by Google

@@ -357,19 +353,19 @@ const servers = [

'stun4.l.google.com:19302',
];
return servers[Math.floor(Math.random()*servers.length)];
]
return servers[Math.floor(Math.random() * servers.length)]
}
exports.get_ipany_re = function (prefix, suffix, modifier) {
if (prefix === undefined) prefix = '';
if (suffix === undefined) suffix = '';
if (modifier === undefined) modifier = 'mg';
if (prefix === undefined) prefix = ''
if (suffix === undefined) suffix = ''
if (modifier === undefined) modifier = 'mg'
/* eslint-disable prefer-template */
return new RegExp(
prefix +
`(` + // capture group
`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))` + // complex ipv4 + ipv6
`)` + // end capture
`${suffix}`,
modifier
);
`(` + // capture group
`(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))` + // complex ipv4 + ipv6
`)` + // end capture
`${suffix}`,
modifier,
)
}

@@ -380,35 +376,25 @@

const errors = []
const promises = []
async function resolveAny (ver) {
try {
const addrs = await dns[`resolve${ver}`](hostname)
for (const a of addrs) {
ips.add(a);
}
return addrs
}
catch (err) {
errors.push(err);
}
}
return Promise.allSettled([
dns.resolve6(hostname),
dns.resolve4(hostname),
]).then((res) => {
res.filter((a) => a.status === 'rejected').map((a) => errors.push(a.reason))
promises.push(resolveAny('4'))
promises.push(resolveAny('6'))
res
.filter((a) => a.status === 'fulfilled')
.map((a) => a.value.map((ip) => ips.add(ip)))
if (done) { // legacy callback API
Promise.all(promises).then((r) => { done(errors, Array.from(ips)) })
}
else { // promise API
// if (process.env.DEBUG && errors.length) console.error(errors)
return Promise.all(promises).then(r => { return Array.from(ips) })
}
if (done) done(errors, Array.from(ips))
return Array.from(ips)
})
}
exports.ipv6_reverse = function (ipv6) {
ipv6 = ipaddr.parse(ipv6);
return ipv6.toNormalizedString()
ipv6 = ipaddr.parse(ipv6)
return ipv6
.toNormalizedString()
.split(':')
.map(function (n) {
return sprintf('%04x', parseInt(n, 16));
return sprintf('%04x', parseInt(n, 16))
})

@@ -418,3 +404,3 @@ .join('')

.reverse()
.join('.');
.join('.')
}

@@ -424,10 +410,9 @@

try {
const ipCheck = ipaddr.parse(ipv6);
if (ipCheck.range() !== 'unicast') return true;
return false;
}
catch (e) {
const ipCheck = ipaddr.parse(ipv6)
if (ipCheck.range() !== 'unicast') return true
return false
} catch (e) {
// If we get an error from parsing, return true for bogus.
console.error(e);
return true;
console.error(e)
return true
}

@@ -437,11 +422,11 @@ }

exports.ip_in_list = function (list, ip) {
if (list === undefined) return false;
if (list === undefined) return false
const isHostname = !net.isIP(ip);
const isArray = Array.isArray(list);
const isHostname = !net.isIP(ip)
const isArray = Array.isArray(list)
// Quick lookup
if (!isArray) {
if (ip in list) return true; // domain or literal IP
if (isHostname) return false; // skip CIDR match
if (ip in list) return true // domain or literal IP
if (isHostname) return false // skip CIDR match
}

@@ -452,32 +437,47 @@

if (isArray) {
item = list[item]; // item is index
if (item === ip) return true; // exact match
item = list[item] // item is index
if (item === ip) return true // exact match
}
if (isHostname) continue; // skip CIDR match
if (isHostname) continue // skip CIDR match
const cidr = item.split('/');
const c_net = cidr[0];
const cidr = item.split('/')
const c_net = cidr[0]
if (!net.isIP(c_net)) continue; // bad config entry
if (net.isIPv4(ip) && net.isIPv6(c_net)) continue;
if (net.isIPv6(ip) && net.isIPv4(c_net)) continue;
if (!net.isIP(c_net)) continue // bad config entry
if (net.isIPv4(ip) && net.isIPv6(c_net)) continue
if (net.isIPv6(ip) && net.isIPv4(c_net)) continue
const c_mask = parseInt(cidr[1], 10) || (net.isIPv6(c_net) ? 128 : 32);
const c_mask = parseInt(cidr[1], 10) || (net.isIPv6(c_net) ? 128 : 32)
if (ipaddr.parse(ip).match(ipaddr.parse(c_net), c_mask)) {
return true;
return true
}
}
return false;
return false
}
exports.get_primary_host_name = function () {
return exports.config.get('me') || os.hostname();
return exports.config.get('me') || os.hostname()
}
exports.get_mx = async function get_mx (raw_domain, cb) {
let domain = raw_domain;
const mxs = [];
function normalizeDomain(raw_domain) {
let domain = raw_domain
if (/@/.test(domain)) {
domain = domain.split('@').pop()
// console.log(`\treduced ${raw_domain} to ${domain}.`)
}
if (/^xn--/.test(domain)) {
// is punycode IDN with ACE, ASCII Compatible Encoding
} else if (domain !== punycode.toASCII(domain)) {
domain = punycode.toASCII(domain)
console.log(`\tACE encoded '${raw_domain}' to '${domain}'`)
}
return domain
}
function fatal_mx_err(err) {
// Possible DNS errors

@@ -497,34 +497,92 @@ // NODATA

if ( /@/.test(domain) ) {
domain = domain.split('@').pop();
// console.log(`\treduced ${raw_domain} to ${domain}.`)
switch (err.code) {
case 'ENODATA':
case 'ENOTFOUND':
// likely a hostname with no MX record, drop through
return false
default:
return err
}
}
if ( /^xn--/.test(domain) ) {
// is punycode IDN with ACE, ASCII Compatible Encoding
exports.get_mx = async (raw_domain, cb) => {
const domain = normalizeDomain(raw_domain)
try {
const exchanges = await dns.resolveMx(domain)
if (exchanges && exchanges.length) {
exchanges.map((e) => (e.from_dns = domain))
if (cb) return cb(null, exchanges)
return exchanges
}
} catch (err) {
// console.error(err.message)
if (fatal_mx_err(err)) {
if (cb) return cb(err, [])
throw err
}
}
else if (domain !== punycode.toASCII(domain)) {
domain = punycode.toASCII(domain);
console.log(`\tACE encoded '${raw_domain}' to '${domain}'`)
// no MX or non-fatal DNS failure
try {
const exchanges = await this.get_implicit_mx(domain)
if (cb) return cb(null, exchanges)
return exchanges
} catch (err) {
if (fatal_mx_err(err)) {
if (cb) return cb(err, [])
throw err
}
}
}
// wrap_mx returns our object with "priority" and "exchange" keys
const wrap_mx = a => a;
let err = null
exports.get_implicit_mx = async (domain) => {
// console.log(`No MX for ${domain}, trying AAAA & A records`)
try {
const addresses = await dns.resolveMx(domain)
if (addresses?.length) {
for (const addr of addresses) {
mxs.push(wrap_mx(addr));
}
const promises = [dns.resolve6(domain), dns.resolve4(domain)]
const r = await Promise.allSettled(promises)
return r
.filter((a) => a.status === 'fulfilled')
.flatMap((a) =>
a.value.map((ip) => ({ priority: 0, exchange: ip, from_dns: domain })),
)
}
exports.resolve_mx_hosts = async (mxes) => {
// for the given list of MX exchanges, resolve the hostnames to IPs
const promises = []
for (const mx of mxes) {
if (!mx.exchange) {
promises.push(mx)
continue
}
if (net.isIP(mx.exchange)) {
promises.push(mx) // already resolved
continue
}
// resolve AAAA and A since mx.exchange is a hostname
promises.push(
dns
.resolve6(mx.exchange)
.then((ips) =>
ips.map((ip) => ({ ...mx, exchange: ip, from_dns: mx.exchange })),
),
)
promises.push(
dns
.resolve4(mx.exchange)
.then((ips) =>
ips.map((ip) => ({ ...mx, exchange: ip, from_dns: mx.exchange })),
),
)
}
catch (e) {
// console.error(e.message)
err = e
}
if (cb) return cb(err, mxs)
return mxs
const settled = await Promise.allSettled(promises)
return settled.filter((s) => s.status === 'fulfilled').flatMap((s) => s.value)
}
{
"name": "haraka-net-utils",
"version": "1.5.4",
"version": "1.6.0",
"description": "haraka network utilities",
"main": "index.js",
"files": [
"CHANGELOG.md"
],
"scripts": {
"test": "npx mocha",
"lint": "npx eslint *.js test",
"lintfix": "npx eslint --fix *.js test",
"versions": "npx dependency-version-checker check"
"format": "npm run prettier:fix && npm run lint:fix",
"lint": "npx eslint@^8 *.js test",
"lint:fix": "npx eslint@^8 *.js test --fix",
"prettier": "npx prettier . --check",
"prettier:fix": "npx prettier . --write --log-level=warn",
"test": "npx mocha@10",
"versions": "npx dependency-version-checker check",
"versions:fix": "npx dependency-version-checker update && npm run prettier:fix"
},

@@ -32,11 +39,9 @@ "repository": {

"devDependencies": {
"eslint": "^8.57.0",
"mocha": "^10.4.0",
"eslint-plugin-haraka": "1.0.15"
"@haraka/eslint-config": "^1.1.3"
},
"dependencies": {
"haraka-config": "^1.1.0",
"haraka-tld": "^1.2.0",
"haraka-tld": "^1.2.1",
"ipaddr.js": "^2.1.0",
"punycode": "^2.3.1",
"punycode.js": "^2.3.1",
"openssl-wrapper": "^0.3.4",

@@ -43,0 +48,0 @@ "sprintf-js": "^1.1.3"

@@ -19,3 +19,3 @@ [![CI][ci-img]][ci-url]

// Convert IPv4 to long
const long = net_utils.ip_to_long('11.22.33.44'); // 185999660
const long = net_utils.ip_to_long('11.22.33.44') // 185999660
```

@@ -27,3 +27,3 @@

// Convert long to IPv4
const ip = net_utils.long_to_ip(185999660); // 11.22.33.44
const ip = net_utils.long_to_ip(185999660) // 11.22.33.44
```

@@ -35,3 +35,3 @@

// Convert decimal to hex
const hex = net_utils.dec_to_hex(20111104); // 132df00
const hex = net_utils.dec_to_hex(20111104) // 132df00
```

@@ -43,3 +43,3 @@

// Convert hex to decimal
const dec = net_utils.hex_to_dec('132df00'); // 20111104
const dec = net_utils.hex_to_dec('132df00') // 20111104
```

@@ -51,5 +51,5 @@

// Is IPv4 address on a local network?
net_utils.is_local_ipv4('127.0.0.200'); // true (localhost)
net_utils.is_local_ipv4('169.254.0.0'); // true (link local)
net_utils.is_local_ipv4('226.0.0.1'); // false
net_utils.is_local_ipv4('127.0.0.200') // true (localhost)
net_utils.is_local_ipv4('169.254.0.0') // true (link local)
net_utils.is_local_ipv4('226.0.0.1') // false
```

@@ -61,5 +61,5 @@

// Is IPv4 address in RFC 1918 reserved private address space?
net_utils.is_private_ipv4('10.0.0.0'); // true
net_utils.is_private_ipv4('192.168.0.0'); // true
net_utils.is_private_ipv4('172.16.0.0'); // true
net_utils.is_private_ipv4('10.0.0.0') // true
net_utils.is_private_ipv4('192.168.0.0') // true
net_utils.is_private_ipv4('172.16.0.0') // true
```

@@ -71,6 +71,6 @@

// Is IPv6 addr on local network?
net_utils.is_local_ipv6('::1'); // true (localhost)
net_utils.is_local_ipv6('fe80::') // true (link local)
net_utils.is_local_ipv6('fc00::') // true (unique local)
net_utils.is_local_ipv6('fd00::') // true (unique local)
net_utils.is_local_ipv6('::1') // true (localhost)
net_utils.is_local_ipv6('fe80::') // true (link local)
net_utils.is_local_ipv6('fc00::') // true (unique local)
net_utils.is_local_ipv6('fd00::') // true (unique local)
```

@@ -97,5 +97,5 @@

// ip can be a host, an IP, or an IPv4 or IPv6 range
net_utils.ip_in_list(object, ip);
net_utils.ip_in_list(array, ip);
net_utils.ip_in_list(tls.no_tls_hosts, '127.0.0.5');
net_utils.ip_in_list(object, ip)
net_utils.ip_in_list(array, ip)
net_utils.ip_in_list(tls.no_tls_hosts, '127.0.0.5')
```

@@ -109,10 +109,9 @@

try {
const ips = await net_utils.get_ips_by_host(domain)
for (const ip of ips) {
// do something with the IPs
}
const ips = await net_utils.get_ips_by_host(domain)
for (const ip of ips) {
// do something with the IPs
}
} catch (err) {
// handle any errors
}
catch (err) {
// handle any errors
}
```

@@ -124,10 +123,9 @@

try {
const mxList = await net_utils.get_mx(domain)
for (const mx of mxList) {
// do something with each mx
}
const mxList = await net_utils.get_mx(domain)
for (const mx of mxList) {
// do something with each mx
}
} catch (err) {
// handle any errors
}
catch (err) {
// handle any errors
}
```

@@ -134,0 +132,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc