diff --git a/.release b/.release index 36bb27a..0fa4e69 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit 36bb27a93862517943e04f24fd67b0df2da6cbbe +Subproject commit 0fa4e690ffabb0157e46d56f18e4f7cfe49ce291 diff --git a/CHANGELOG.md b/CHANGELOG.md index 761abc0..2e64e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Unreleased +### [1.7.0] - 2024-04-29 + +- feat: added HarakaMx #89 +- feat: add add_line_processor, aka line_socket.setup_line_processor +- fix(get_public_ip): set timeout in stun request, fixes #84 +- test: added get_implicit_mx tests #89 +- change: get_mx: don't filter implicit MX errors #89 + ### [1.6.0] - 2024-04-17 - feat: normalizeDomain, for punycode/IDN names + - feat: get_mx now _also_ returns implicit MX records - feat: added get_implicit_mx - feat: added resolve_mx_hosts @@ -62,88 +71,88 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). - chore(ci): populate test matrix with Node.js LTS versions - chore(ci): limit dependabot updates to production deps -#### [1.3.5] - 2022-05-27 +### [1.3.5] - 2022-05-27 - chore(ci): use shared GHA workflows - style(es6): use dns.promises internally - dep(async): replace async dependency with Promise.all - doc(README): use code fences around examples (vs indention) -#### [1.3.4] - 2022-01-05 +### [1.3.4] - 2022-01-05 - promisify get_ips_by_host (backwards compatible) -#### [1.3.3] - 2020-01-05 +### [1.3.3] - 2020-01-05 - refactored is_local_host function to return a promise instead of using a callback #65 -#### [1.3.2] - 2021-12-20 +### [1.3.2] - 2021-12-20 - add is_local_host function #63 -#### [1.3.1] - 2021-10-13 +### [1.3.1] - 2021-10-13 - get_mx: wrap dns.resolveMx in a try haraka/Haraka#2985 - add .release scripts - add GH workflow, publish release to NPM upon merge to master -#### 1.3.0 - 2021-01-23 +### 1.3.0 - 2021-01-23 - Support passing an array to ip_in_list #60 -#### 1.2.4 - 2021-01-14 +### 1.2.4 - 2021-01-14 - add "any" IP to is_local_ip - add TEST-NET-[1-3] to is_private_ip -#### 1.2.3 - 2020-12-19 +### 1.2.3 - 2020-12-19 - fix: restore the tests wrapping the resolveMX iterable -#### 1.2.2 - 2020-12-15 +### 1.2.2 - 2020-12-15 - get_mx: do not include implicit MX -#### [1.2.1] - 2020-11-17 +### [1.2.1] - 2020-11-17 - bump ipaddr.js to 2.0.0 #56 -#### [1.2.0] - 2020-06-23 +### [1.2.0] - 2020-06-23 - added get_mx - remove deprecated load_tls_ini - remove deprecated tls_ini_section_with_defaults -#### 1.1.5 - 2020-04-11 +### 1.1.5 - 2020-04-11 - ipv6_bogus: handle parsing broken ipv6 addresses #49 - update async to version 3.0.1 #43 -#### 1.1.4 - 2019-04-04 +### 1.1.4 - 2019-04-04 - stop is_private_ip from checking if the IP is bound to a local network interface -#### 1.1.3 - 2019-03-01 +### 1.1.3 - 2019-03-01 - is_local_ip checks local network interfaces too -#### 1.1.2 - 2018-11-03 +### 1.1.2 - 2018-11-03 - add is_local_ip -#### 1.1.1 - 2018-07-19 +### 1.1.1 - 2018-07-19 - ip_in_list doesn't throw on empty list -#### 1.1.0 - 2018-04-11 +### 1.1.0 - 2018-04-11 - add get_primary_host_name haraka/Haraka#2380 -#### 1.0.14 - 2018-01-25 +### 1.0.14 - 2018-01-25 - restore tls_ini_section_with_defaults function (deprecated since Haraka 2.0.17) -#### 1.0.13 - 2018-01-19 +### 1.0.13 - 2018-01-19 - get_public_ip: assign timer before calling connect #29 - avoid race where timeout isn't cleared because stun connect errors immediately @@ -152,40 +161,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). - eslint updates #25, #27 - improved x509 parser #22 -#### 1.0.10 - 2017-07-27 +### 1.0.10 - 2017-07-27 - added vs-stun as optional dep (from Haraka) #21 -#### 1.0.9 - 2017-06-16 +### 1.0.9 - 2017-06-16 - lint fixes for compat with eslint 4 #18 -#### 1.0.8 - 2017-03-08 +### 1.0.8 - 2017-03-08 - skip loading expired x509 (TLS) certs - make TLS cert dir configurable - rename certs -> cert (be consistent with haraka/plugins/tls) - store cert/key as buffers (was strings) -#### 1.0.7 - 2017-03-08 +### 1.0.7 - 2017-03-08 - handle undefined tls.ini section -#### 1.0.6 - 2017-03-04 +### 1.0.6 - 2017-03-04 - add tls_ini_section_with_defaults() - add load_tls_dir() - add parse_x509_names() -#### 1.0.5 - 2016-11-20 +### 1.0.5 - 2016-11-20 - add enableSNI TLS option -#### 1.0.4 - 2016-10-25 +### 1.0.4 - 2016-10-25 - initialize TLS opts in (section != main) as booleans -#### 1.0.3 - 2016-10-25 +### 1.0.3 - 2016-10-25 - added tls.ini loading @@ -197,7 +206,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). [1.3.4]: https://github.com/haraka/haraka-net-utils/releases/tag/1.3.4 [1.3.5]: https://github.com/haraka/haraka-net-utils/releases/tag/1.3.5 [1.3.6]: https://github.com/haraka/haraka-net-utils/releases/tag/1.3.6 -[1.3.7]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.3.7 +[1.3.7]: https://github.com/haraka/haraka-net-utils/releases/tag/1.3.7 [1.4.0]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.4.0 [1.4.1]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.4.1 [1.5.0]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.5.0 @@ -206,3 +215,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). [1.5.3]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.5.3 [1.5.4]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.5.4 [1.6.0]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.6.0 +[1.7.0]: https://github.com/haraka/haraka-net-utils/releases/tag/v1.7.0 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ac187dd..90ae255 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,7 +2,7 @@ This handcrafted artisinal software is brought to you by: -|
msimerson (57) |
baudehlo (4) |
DoobleD (2) |
lnedry (2) |
Juerd (1) |
olsonpm (1) |
typingArtist (1) | +|
msimerson (58) |
baudehlo (4) |
DoobleD (2) |
lnedry (2) |
Juerd (1) |
olsonpm (1) |
typingArtist (1) | | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | this file is maintained by [.release](https://github.com/msimerson/.release) diff --git a/README.md b/README.md index 96ee3d8..404adff 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,50 @@ try { } ``` +### HarakaMx + +An object class representing a MX. HarakaMx objects may contain the following properties: + +```js +{ + exchange: '', // required: a FQDN or IP address + path: '', // the file path to a socket + priority: 0, // integer, a MX priority. + port: 25, // integer: an alternate port + bind: '', // an outbound IP address to bind to + bind_helo: '', // an outbound helo hostname + using_lmtp: false, // boolean, specify LMTP delivery + auth_user: '', // an AUTH username (required if AUTH is desired) + auth_pass: '', // an AUTH password (required if AUTH is desired) + auth_type: '', // an AUTH type that should be used with the MX. + from_dns: '', // the DNS name from which the MX was queried +} +``` + +Create a HarakaMx object in The Usual Way: + +```js +const nu = require('haraka-net-utils') +const myMx = new nu.HarakaMx(parameter) +``` + +The parameter can be one of: + +- A string in any of the following formats: + - hostname + - hostname:port + - IPv4 + - IPv4:port + - [IPv6] + - [IPv6]: port +- A [URL](https://nodejs.org/docs/latest-v20.x/api/url.html) string + - smtp://mail.example.com:25 + - lmtp://int-mail.example.com:24 + - smtp://user:pass@host.example.com:587 +- An object, containing at least an exchange, and any of the other properties listed at the top of this section. + +An optional second parameter is an alias for from_dns. + [ci-img]: https://github.com/haraka/haraka-net-utils/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/haraka/haraka-net-utils/actions/workflows/ci.yml [cov-img]: https://codecov.io/github/haraka/haraka-net-utils/coverage.svg diff --git a/index.js b/index.js index 77955e7..fa9e7c8 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,9 @@ 'use strict' -// node.js built-ins -const { Resolver } = require('dns').promises +const { Resolver } = require('node:dns').promises const dns = new Resolver({ timeout: 25000, tries: 1 }) -const net = require('net') -const os = require('os') -const punycode = require('punycode.js') +const net = require('node:net') +const os = require('node:os') // npm modules const ipaddr = require('ipaddr.js') @@ -259,97 +257,7 @@ exports.same_ipv4_network = function (ip, ipList) { return false } -exports.get_public_ip_async = async function () { - 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 - if (smtpIni.public_ip) { - this.public_ip = smtpIni.public_ip - return this.public_ip - } - - // Initialise cache value to null to prevent running - // should we hit a timeout or the module isn't installed. - 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}`) - return - } - - const timeout = 10 - const timer = setTimeout(() => { - return new Error('STUN timeout') - }, timeout * 1000) - - // Connect to STUN Server - const res = await this.stun.request(get_stun_server()) - this.public_ip = res.getXorAddress().address - clearTimeout(timer) - return this.public_ip -} - -exports.get_public_ip = async function (cb) { - if (!cb) return exports.get_public_ip_async() - - 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 - if (smtpIni.public_ip) { - nu.public_ip = smtpIni.public_ip - return cb(null, nu.public_ip) - } - - // Initialise cache value to null to prevent running - // should we hit a timeout or the module isn't installed. - nu.public_ip = null - - try { - 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) - } - - const timeout = 10 - const timer = setTimeout(() => { - 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) - - nu.public_ip = res.getXorAddress().address - cb(null, nu.public_ip) - }) -} - -function get_stun_server() { - // STUN servers by Google - const servers = [ - 'stun.l.google.com:19302', - 'stun1.l.google.com:19302', - 'stun2.l.google.com:19302', - 'stun3.l.google.com:19302', - 'stun4.l.google.com:19302', - ] - 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' - /* eslint-disable prefer-template */ +exports.get_ipany_re = function (prefix = '', suffix = '', modifier = 'mg') { return new RegExp( prefix + `(` + // capture group @@ -446,127 +354,34 @@ exports.get_primary_host_name = function () { return exports.config.get('me') || os.hostname() } -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 +for (const l of ['get_mx', 'get_implicit_mx', 'resolve_mx_hosts']) { + exports[l] = require('./lib/get_mx')[l] } -function fatal_mx_err(err) { - // Possible DNS errors - // NODATA - // FORMERR - // BADRESP - // NOTFOUND - // BADNAME - // TIMEOUT - // CONNREFUSED - // NOMEM - // DESTRUCTION - // NOTIMP - // EREFUSED - // SERVFAIL - - switch (err.code) { - case 'ENODATA': - case 'ENOTFOUND': - // likely a hostname with no MX record, drop through - return false - default: - return err - } -} +exports.get_public_ip = require('./lib/get_public_ip').get_public_ip -exports.get_mx = async (raw_domain, cb) => { - const domain = normalizeDomain(raw_domain) +exports.get_public_ip_async = require('./lib/get_public_ip').get_public_ip_async - 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 - } - } +exports.HarakaMx = require('./lib/HarakaMx') - // 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 - } - } -} +exports.add_line_processor = (socket) => { + const line_regexp = /^([^\n]*\n)/ // utils.line_regexp + let current_data = '' -exports.get_implicit_mx = async (domain) => { - // console.log(`No MX for ${domain}, trying AAAA & A records`) - - 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 + socket.on('data', (data) => { + current_data += data + let results + while ((results = line_regexp.exec(current_data))) { + const this_line = results[1] + current_data = current_data.slice(this_line.length) + socket.emit('line', this_line) } + }) - if (net.isIP(mx.exchange)) { - promises.push(mx) // already resolved - continue + socket.on('end', () => { + if (current_data.length) { + socket.emit('line', current_data) } - - // 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 })), - ), - ) - } - - const settled = await Promise.allSettled(promises) - - return settled.filter((s) => s.status === 'fulfilled').flatMap((s) => s.value) + current_data = '' + }) } diff --git a/lib/HarakaMx.js b/lib/HarakaMx.js new file mode 100644 index 0000000..239f298 --- /dev/null +++ b/lib/HarakaMx.js @@ -0,0 +1,99 @@ +'use strict' + +const net = require('node:net') +const os = require('node:os') +const url = require('node:url') + +const config = require('haraka-config') + +class HarakaMx { + constructor(obj = {}, domain) { + if (obj instanceof HarakaMx) return obj + + switch (typeof obj) { + case 'string': + ;/mtp:\/\//.test(obj) ? this.fromUrl(obj) : this.fromString(obj) + break + case 'object': + this.fromObject(obj) + break + } + + if (this.priority === undefined) this.priority = 0 + + if (domain && this.from_dns === undefined) { + this.from_dns = domain.toLowerCase() + } + + if (process.env.NODE_ENV !== 'test') { + if (this.bind_helo === undefined) { + this.bind_helo = config.get('me') || os.hostname() + } + } + } + + fromObject(obj) { + for (const prop of [ + 'exchange', + 'path', + 'priority', + 'port', + 'bind', + 'bind_helo', + 'using_lmtp', + 'auth_user', + 'auth_pass', + 'auth_type', + 'from_dns', + ]) { + if (obj[prop] !== undefined) this[prop] = obj[prop] + } + } + + fromString(str) { + const matches = /^\[?(.*?)\]?(?::(24|25|465|587|\d{4,5}))?$/.exec(str) + if (matches) { + this.exchange = matches[1].toLowerCase() + if (matches[2]) this.port = parseInt(matches[2]) + } else { + this.exchange = str + } + } + + fromUrl(str) { + const dest = new url.URL(str) + + switch (dest.protocol) { + case 'smtp:': + break + case 'lmtp:': + this.using_lmtp = true + break + } + + if (dest.hostname) this.exchange = dest.hostname.toLowerCase() + if (dest.port) this.port = parseInt(dest.port) + if (dest.username) this.auth_user = dest.username + if (dest.password) this.auth_pass = dest.password + } + + toUrl() { + const host = net.isIPv6(this.exchange) + ? `[${this.exchange}]` + : this.exchange + if (this.path) { + return new url.URL(`file://${host || 'localhost'}${this.path}`).href + } + const proto = this.using_lmtp ? 'lmtp://' : 'smtp://' + const auth = this.auth_user ? `${this.auth_user}:****@` : '' + const port = this.port ? `:${this.port}` : '' + return new url.URL(`${proto}${auth}${host}${port}`).href + } + + toString() { + const from_dns = this.from_dns ? ` (via DNS)` : '' + return `MX ${this.priority} ${this.toUrl()}${from_dns}` + } +} + +module.exports = HarakaMx diff --git a/lib/get_mx.js b/lib/get_mx.js new file mode 100644 index 0000000..91bdc26 --- /dev/null +++ b/lib/get_mx.js @@ -0,0 +1,128 @@ +'use strict' + +const { Resolver } = require('node:dns').promises +const dns = new Resolver({ timeout: 25000, tries: 1 }) +const net = require('node:net') + +const punycode = require('punycode.js') + +const HarakaMx = require('./HarakaMx') + +exports.get_mx = async (raw_domain, cb) => { + const domain = normalizeDomain(raw_domain) + + try { + let exchanges = await dns.resolveMx(domain) + if (exchanges && exchanges.length) { + exchanges = exchanges.map((e) => new HarakaMx(e, domain)) + if (cb) return cb(null, exchanges) + return exchanges + } + // no MX record(s), fall through + } catch (err) { + if (fatal_mx_err(err)) { + if (cb) return cb(err, []) + throw err + } + // non-terminal DNS failure, fall through + } + + const exchanges = await this.get_implicit_mx(domain) + if (cb) return cb(null, exchanges) + return exchanges +} + +exports.get_implicit_mx = async (domain) => { + // console.log(`No MX for ${domain}, trying AAAA & A records`) + + const r = await Promise.allSettled([ + dns.resolve6(domain), + dns.resolve4(domain), + ]) + + return r + .filter((a) => a.status === 'fulfilled') + .flatMap((a) => a.value.map((ip) => new HarakaMx(ip, 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) { + // console.error(`MX without an exchange. could be a socket`) + 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 })), + ), + ) + } + + const settled = await Promise.allSettled(promises) + + return settled.filter((s) => s.status === 'fulfilled').flatMap((s) => s.value) +} + +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 + // NODATA + // FORMERR + // BADRESP + // NOTFOUND + // BADNAME + // TIMEOUT + // CONNREFUSED + // NOMEM + // DESTRUCTION + // NOTIMP + // EREFUSED + // SERVFAIL + + switch (err.code) { + case 'ENODATA': + case 'ENOTFOUND': + // likely a hostname with no MX record, drop through + return false + default: + return err + } +} diff --git a/lib/get_public_ip.js b/lib/get_public_ip.js new file mode 100644 index 0000000..7012740 --- /dev/null +++ b/lib/get_public_ip.js @@ -0,0 +1,93 @@ +'use strict' + +exports.config = require('haraka-config') + +exports.get_public_ip_async = async function () { + 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 + if (smtpIni.public_ip) { + this.public_ip = smtpIni.public_ip + return this.public_ip + } + + // Initialise cache value to null to prevent running + // should we hit a timeout or the module isn't installed. + 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}`) + return + } + + const timeout = 10 + const timer = setTimeout(() => { + return new Error('STUN timeout') + }, timeout * 1000) + + // Connect to STUN Server + const res = await this.stun.request(get_stun_server(), { + maxTimeout: (timeout - 1) * 1000, + }) + this.public_ip = res.getXorAddress().address + clearTimeout(timer) + return this.public_ip +} + +exports.get_public_ip = async function (cb) { + if (!cb) return exports.get_public_ip_async() + + if (this.public_ip !== undefined) return cb(null, 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 + if (smtpIni.public_ip) { + this.public_ip = smtpIni.public_ip + return cb(null, this.public_ip) + } + + // Initialise cache value to null to prevent running + // should we hit a timeout or the module isn't installed. + 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}`) + return cb(e) + } + + const timeout = 10 + const timer = setTimeout(() => { + return cb(new Error('STUN timeout')) + }, timeout * 1000) + + // Connect to STUN Server + this.stun.request( + get_stun_server(), + { maxTimeout: (timeout - 1) * 1000 }, + (error, res) => { + if (timer) clearTimeout(timer) + if (error) return cb(error) + + this.public_ip = res.getXorAddress().address + cb(null, this.public_ip) + }, + ) +} + +function get_stun_server() { + const servers = [ + 'stun.l.google.com:19302', + 'stun1.l.google.com:19302', + 'stun2.l.google.com:19302', + 'stun3.l.google.com:19302', + 'stun4.l.google.com:19302', + ] + return servers[Math.floor(Math.random() * servers.length)] +} diff --git a/package.json b/package.json index dd51bb9..7b30189 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "haraka-net-utils", - "version": "1.6.0", + "version": "1.7.0", "description": "haraka network utilities", "main": "index.js", "files": [ - "CHANGELOG.md" + "CHANGELOG.md", + "lib" ], "scripts": { "format": "npm run prettier:fix && npm run lint:fix", @@ -36,12 +37,12 @@ }, "homepage": "https://github.com/haraka/haraka-net-utils#readme", "devDependencies": { - "@haraka/eslint-config": "^1.1.3" + "@haraka/eslint-config": "^1.1.5" }, "dependencies": { - "haraka-config": "^1.1.0", + "haraka-config": "^1.2.4", "haraka-tld": "^1.2.1", - "ipaddr.js": "^2.1.0", + "ipaddr.js": "^2.2.0", "punycode.js": "^2.3.1", "openssl-wrapper": "^0.3.4", "sprintf-js": "^1.1.3" diff --git a/test/get_ip_any.js b/test/get_ip_any.js new file mode 100644 index 0000000..7314e58 --- /dev/null +++ b/test/get_ip_any.js @@ -0,0 +1,627 @@ +const assert = require('node:assert') +const net = require('node:net') + +const ip_fixtures = [ + [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876 '], + [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0'], + [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876'], + [false, ' 2001:0:1234::C1C0:ABCD:876 '], + [false, ' 2001:0:1234::C1C0:ABCD:876'], + [false, ''], + [false, "':10.0.0.1"], + [false, '---'], + [false, '02001:0000:1234:0000:0000:C1C0:ABCD:0876'], + [false, '1.2.3.4:1111:2222:3333:4444::5555'], + [false, '1.2.3.4:1111:2222:3333::5555'], + [false, '1.2.3.4:1111:2222::5555'], + [false, '1.2.3.4:1111::5555'], + [false, '1.2.3.4::'], + [false, '1.2.3.4::5555'], + [false, '1111'], + [false, '11112222:3333:4444:5555:6666:1.2.3.4'], + [false, '11112222:3333:4444:5555:6666:7777:8888'], + [false, '1111:'], + [false, '1111:1.2.3.4'], + [false, '1111:2222'], + [false, '1111:22223333:4444:5555:6666:1.2.3.4'], + [false, '1111:22223333:4444:5555:6666:7777:8888'], + [false, '1111:2222:'], + [false, '1111:2222:1.2.3.4'], + [false, '1111:2222:3333'], + [false, '1111:2222:33334444:5555:6666:1.2.3.4'], + [false, '1111:2222:33334444:5555:6666:7777:8888'], + [false, '1111:2222:3333:'], + [false, '1111:2222:3333:1.2.3.4'], + [false, '1111:2222:3333:4444'], + [false, '1111:2222:3333:44445555:6666:1.2.3.4'], + [false, '1111:2222:3333:44445555:6666:7777:8888'], + [false, '1111:2222:3333:4444:'], + [false, '1111:2222:3333:4444:1.2.3.4'], + [false, '1111:2222:3333:4444:5555'], + [false, '1111:2222:3333:4444:55556666:1.2.3.4'], + [false, '1111:2222:3333:4444:55556666:7777:8888'], + [false, '1111:2222:3333:4444:5555:'], + [false, '1111:2222:3333:4444:5555:1.2.3.4'], + [false, '1111:2222:3333:4444:5555:6666'], + [false, '1111:2222:3333:4444:5555:66661.2.3.4'], + [false, '1111:2222:3333:4444:5555:66667777:8888'], + [false, '1111:2222:3333:4444:5555:6666:'], + [false, '1111:2222:3333:4444:5555:6666:00.00.00.00'], + [false, '1111:2222:3333:4444:5555:6666:000.000.000.000'], + [false, '1111:2222:3333:4444:5555:6666:1.2.3.4.5'], + [false, '1111:2222:3333:4444:5555:6666:255.255.255255'], + [false, '1111:2222:3333:4444:5555:6666:255.255255.255'], + [false, '1111:2222:3333:4444:5555:6666:255255.255.255'], + [false, '1111:2222:3333:4444:5555:6666:256.256.256.256'], + [false, '1111:2222:3333:4444:5555:6666:7777'], + [false, '1111:2222:3333:4444:5555:6666:77778888'], + [false, '1111:2222:3333:4444:5555:6666:7777:'], + [false, '1111:2222:3333:4444:5555:6666:7777:1.2.3.4'], + [false, '1111:2222:3333:4444:5555:6666:7777:8888:'], + [false, '1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4'], + [false, '1111:2222:3333:4444:5555:6666:7777:8888:9999'], + [false, '1111:2222:3333:4444:5555:6666:7777:8888::'], + [false, '1111:2222:3333:4444:5555:6666:7777:::'], + [false, '1111:2222:3333:4444:5555:6666::1.2.3.4'], + [false, '1111:2222:3333:4444:5555:6666::8888:'], + [false, '1111:2222:3333:4444:5555:6666:::'], + [false, '1111:2222:3333:4444:5555:6666:::8888'], + [false, '1111:2222:3333:4444:5555::7777:8888:'], + [false, '1111:2222:3333:4444:5555::7777::'], + [false, '1111:2222:3333:4444:5555::8888:'], + [false, '1111:2222:3333:4444:5555:::'], + [false, '1111:2222:3333:4444:5555:::1.2.3.4'], + [false, '1111:2222:3333:4444:5555:::7777:8888'], + [false, '1111:2222:3333:4444::5555:'], + [false, '1111:2222:3333:4444::6666:7777:8888:'], + [false, '1111:2222:3333:4444::6666:7777::'], + [false, '1111:2222:3333:4444::6666::8888'], + [false, '1111:2222:3333:4444::7777:8888:'], + [false, '1111:2222:3333:4444::8888:'], + [false, '1111:2222:3333:4444:::'], + [false, '1111:2222:3333:4444:::6666:1.2.3.4'], + [false, '1111:2222:3333:4444:::6666:7777:8888'], + [false, '1111:2222:3333::5555:'], + [false, '1111:2222:3333::5555:6666:7777:8888:'], + [false, '1111:2222:3333::5555:6666:7777::'], + [false, '1111:2222:3333::5555:6666::8888'], + [false, '1111:2222:3333::5555::1.2.3.4'], + [false, '1111:2222:3333::5555::7777:8888'], + [false, '1111:2222:3333::6666:7777:8888:'], + [false, '1111:2222:3333::7777:8888:'], + [false, '1111:2222:3333::8888:'], + [false, '1111:2222:3333:::'], + [false, '1111:2222:3333:::5555:6666:1.2.3.4'], + [false, '1111:2222:3333:::5555:6666:7777:8888'], + [false, '1111:2222::4444:5555:6666:7777:8888:'], + [false, '1111:2222::4444:5555:6666:7777::'], + [false, '1111:2222::4444:5555:6666::8888'], + [false, '1111:2222::4444:5555::1.2.3.4'], + [false, '1111:2222::4444:5555::7777:8888'], + [false, '1111:2222::4444::6666:1.2.3.4'], + [false, '1111:2222::4444::6666:7777:8888'], + [false, '1111:2222::5555:'], + [false, '1111:2222::5555:6666:7777:8888:'], + [false, '1111:2222::6666:7777:8888:'], + [false, '1111:2222::7777:8888:'], + [false, '1111:2222::8888:'], + [false, '1111:2222:::'], + [false, '1111:2222:::4444:5555:6666:1.2.3.4'], + [false, '1111:2222:::4444:5555:6666:7777:8888'], + [false, '1111::3333:4444:5555:6666:7777:8888:'], + [false, '1111::3333:4444:5555:6666:7777::'], + [false, '1111::3333:4444:5555:6666::8888'], + [false, '1111::3333:4444:5555::1.2.3.4'], + [false, '1111::3333:4444:5555::7777:8888'], + [false, '1111::3333:4444::6666:1.2.3.4'], + [false, '1111::3333:4444::6666:7777:8888'], + [false, '1111::3333::5555:6666:1.2.3.4'], + [false, '1111::3333::5555:6666:7777:8888'], + [false, '1111::4444:5555:6666:7777:8888:'], + [false, '1111::5555:'], + [false, '1111::5555:6666:7777:8888:'], + [false, '1111::6666:7777:8888:'], + [false, '1111::7777:8888:'], + [false, '1111::8888:'], + [false, '1111:::'], + [false, '1111:::3333:4444:5555:6666:1.2.3.4'], + [false, '1111:::3333:4444:5555:6666:7777:8888'], + [false, '123'], + [false, '12345::6:7:8'], + [false, '192.168.0.256'], + [false, '192.168.256.0'], + [false, '1:2:3:4:5:6:7:8:9'], + [false, '1:2:3::4:5:6:7:8:9'], + [false, '1:2:3::4:5::7:8'], + [false, '1::1.2.256.4'], + [false, '1::1.2.3.256'], + [false, '1::1.2.3.300'], + [false, '1::1.2.3.900'], + [false, '1::1.2.300.4'], + [false, '1::1.2.900.4'], + [false, '1::1.256.3.4'], + [false, '1::1.300.3.4'], + [false, '1::1.900.3.4'], + [false, '1::256.2.3.4'], + [false, '1::260.2.3.4'], + [false, '1::2::3'], + [false, '1::300.2.3.4'], + [false, '1::300.300.300.300'], + [false, '1::3000.30.30.30'], + [false, '1::400.2.3.4'], + [false, '1::5:1.2.256.4'], + [false, '1::5:1.2.3.256'], + [false, '1::5:1.2.3.300'], + [false, '1::5:1.2.3.900'], + [false, '1::5:1.2.300.4'], + [false, '1::5:1.2.900.4'], + [false, '1::5:1.256.3.4'], + [false, '1::5:1.300.3.4'], + [false, '1::5:1.900.3.4'], + [false, '1::5:256.2.3.4'], + [false, '1::5:260.2.3.4'], + [false, '1::5:300.2.3.4'], + [false, '1::5:300.300.300.300'], + [false, '1::5:3000.30.30.30'], + [false, '1::5:400.2.3.4'], + [false, '1::5:900.2.3.4'], + [false, '1::900.2.3.4'], + [false, '1:::3:4:5'], + [false, '2001:0000:1234: 0000:0000:C1C0:ABCD:0876'], + [false, '2001:0000:1234:0000:00001:C1C0:ABCD:0876'], + [false, '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0'], + [false, '2001:1:1:1:1:1:255Z255X255Y255'], + [false, '2001::FFD3::57ab'], + [false, '2001:DB8:0:0:8:800:200C:417A:221'], + [false, '2001:db8:85a3::8a2e:37023:7334'], + [false, '2001:db8:85a3::8a2e:370k:7334'], + [false, '255.256.255.255'], + [false, '256.255.255.255'], + [false, '3ffe:0b00:0000:0001:0000:0000:000a'], + [false, '3ffe:b00::1::a'], + [false, ':'], + [false, ':1.2.3.4'], + [false, ':1111:2222:3333:4444:5555:6666:1.2.3.4'], + [false, ':1111:2222:3333:4444:5555:6666:7777:8888'], + [false, ':1111:2222:3333:4444:5555:6666:7777::'], + [false, ':1111:2222:3333:4444:5555:6666::'], + [false, ':1111:2222:3333:4444:5555:6666::8888'], + [false, ':1111:2222:3333:4444:5555::'], + [false, ':1111:2222:3333:4444:5555::1.2.3.4'], + [false, ':1111:2222:3333:4444:5555::7777:8888'], + [false, ':1111:2222:3333:4444:5555::8888'], + [false, ':1111:2222:3333:4444::'], + [false, ':1111:2222:3333:4444::1.2.3.4'], + [false, ':1111:2222:3333:4444::5555'], + [false, ':1111:2222:3333:4444::6666:1.2.3.4'], + [false, ':1111:2222:3333:4444::6666:7777:8888'], + [false, ':1111:2222:3333:4444::7777:8888'], + [false, ':1111:2222:3333:4444::8888'], + [false, ':1111:2222:3333::'], + [false, ':1111:2222:3333::1.2.3.4'], + [false, ':1111:2222:3333::5555'], + [false, ':1111:2222:3333::5555:6666:1.2.3.4'], + [false, ':1111:2222:3333::5555:6666:7777:8888'], + [false, ':1111:2222:3333::6666:1.2.3.4'], + [false, ':1111:2222:3333::6666:7777:8888'], + [false, ':1111:2222:3333::7777:8888'], + [false, ':1111:2222:3333::8888'], + [false, ':1111:2222::'], + [false, ':1111:2222::1.2.3.4'], + [false, ':1111:2222::4444:5555:6666:1.2.3.4'], + [false, ':1111:2222::4444:5555:6666:7777:8888'], + [false, ':1111:2222::5555'], + [false, ':1111:2222::5555:6666:1.2.3.4'], + [false, ':1111:2222::5555:6666:7777:8888'], + [false, ':1111:2222::6666:1.2.3.4'], + [false, ':1111:2222::6666:7777:8888'], + [false, ':1111:2222::7777:8888'], + [false, ':1111:2222::8888'], + [false, ':1111::'], + [false, ':1111::1.2.3.4'], + [false, ':1111::3333:4444:5555:6666:1.2.3.4'], + [false, ':1111::3333:4444:5555:6666:7777:8888'], + [false, ':1111::4444:5555:6666:1.2.3.4'], + [false, ':1111::4444:5555:6666:7777:8888'], + [false, ':1111::5555'], + [false, ':1111::5555:6666:1.2.3.4'], + [false, ':1111::5555:6666:7777:8888'], + [false, ':1111::6666:1.2.3.4'], + [false, ':1111::6666:7777:8888'], + [false, ':1111::7777:8888'], + [false, ':1111::8888'], + [false, ':2222:3333:4444:5555:6666:1.2.3.4'], + [false, ':2222:3333:4444:5555:6666:7777:8888'], + [false, ':3333:4444:5555:6666:1.2.3.4'], + [false, ':3333:4444:5555:6666:7777:8888'], + [false, ':4444:5555:6666:1.2.3.4'], + [false, ':4444:5555:6666:7777:8888'], + [false, ':5555:6666:1.2.3.4'], + [false, ':5555:6666:7777:8888'], + [false, ':6666:1.2.3.4'], + [false, ':6666:7777:8888'], + [false, ':7777:8888'], + [false, ':8888'], + [false, '::.'], + [false, '::..'], + [false, '::...'], + [false, '::...4'], + [false, '::..3.'], + [false, '::..3.4'], + [false, '::.2..'], + [false, '::.2.3.'], + [false, '::.2.3.4'], + [false, '::1...'], + [false, '::1.2..'], + [false, '::1.2.256.4'], + [false, '::1.2.3.'], + [false, '::1.2.3.256'], + [false, '::1.2.3.300'], + [false, '::1.2.3.900'], + [false, '::1.2.300.4'], + [false, '::1.2.900.4'], + [false, '::1.256.3.4'], + [false, '::1.300.3.4'], + [false, '::1.900.3.4'], + [false, '::1111:2222:3333:4444:5555:6666::'], + [false, '::2222:3333:4444:5555:6666:7777:1.2.3.4'], + [false, '::2222:3333:4444:5555:6666:7777:8888:'], + [false, '::2222:3333:4444:5555:6666:7777:8888:9999'], + [false, '::2222:3333:4444:5555:7777:8888::'], + [false, '::2222:3333:4444:5555:7777::8888'], + [false, '::2222:3333:4444:5555::1.2.3.4'], + [false, '::2222:3333:4444:5555::7777:8888'], + [false, '::2222:3333:4444::6666:1.2.3.4'], + [false, '::2222:3333:4444::6666:7777:8888'], + [false, '::2222:3333::5555:6666:1.2.3.4'], + [false, '::2222:3333::5555:6666:7777:8888'], + [false, '::2222::4444:5555:6666:1.2.3.4'], + [false, '::2222::4444:5555:6666:7777:8888'], + [false, '::256.2.3.4'], + [false, '::260.2.3.4'], + [false, '::300.2.3.4'], + [false, '::300.300.300.300'], + [false, '::3000.30.30.30'], + [false, '::3333:4444:5555:6666:7777:8888:'], + [false, '::400.2.3.4'], + [false, '::4444:5555:6666:7777:8888:'], + [false, '::5555:'], + [false, '::5555:6666:7777:8888:'], + [false, '::6666:7777:8888:'], + [false, '::7777:8888:'], + [false, '::8888:'], + [false, '::900.2.3.4'], + [false, ':::'], + [false, ':::1.2.3.4'], + [false, ':::2222:3333:4444:5555:6666:1.2.3.4'], + [false, ':::2222:3333:4444:5555:6666:7777:8888'], + [false, ':::3333:4444:5555:6666:7777:8888'], + [false, ':::4444:5555:6666:1.2.3.4'], + [false, ':::4444:5555:6666:7777:8888'], + [false, ':::5555'], + [false, ':::5555:6666:1.2.3.4'], + [false, ':::5555:6666:7777:8888'], + [false, ':::6666:1.2.3.4'], + [false, ':::6666:7777:8888'], + [false, ':::7777:8888'], + [false, ':::8888'], + [false, '::ffff:192x168.1.26'], + [false, '::ffff:2.3.4'], + [false, '::ffff:257.1.2.3'], + [false, 'FF01::101::2'], + [false, 'FF02:0000:0000:0000:0000:0000:0000:0000:0001'], + [false, 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4'], + [false, 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX'], + [false, 'fe80:0000:0000:0000:0204:61ff:254.157.241.086'], + [false, 'fe80::4413:c8ae:2821:5852%10'], + [false, 'ldkfj'], + [false, 'mydomain.com'], + [false, 'test.mydomain.com'], + [true, '0000:0000:0000:0000:0000:0000:0000:0000'], + [true, '0000:0000:0000:0000:0000:0000:0000:0001'], + [true, '0:0:0:0:0:0:0:0'], + [true, '0:0:0:0:0:0:0:1'], + [true, '0:0:0:0:0:0:0::'], + [true, '0:0:0:0:0:0:13.1.68.3'], + [true, '0:0:0:0:0:0::'], + [true, '0:0:0:0:0::'], + [true, '0:0:0:0:0:FFFF:129.144.52.38'], + [true, '0:0:0:0::'], + [true, '0:0:0::'], + [true, '0:0::'], + [true, '0::'], + [true, '0:a:b:c:d:e:f::'], + [true, '1.2.3.4'], + [true, '1111:2222:3333:4444:5555:6666:123.123.123.123'], + [true, '1111:2222:3333:4444:5555:6666:7777:8888'], + [true, '1111:2222:3333:4444:5555:6666:7777::'], + [true, '1111:2222:3333:4444:5555:6666::'], + [true, '1111:2222:3333:4444:5555:6666::8888'], + [true, '1111:2222:3333:4444:5555::'], + [true, '1111:2222:3333:4444:5555::123.123.123.123'], + [true, '1111:2222:3333:4444:5555::7777:8888'], + [true, '1111:2222:3333:4444:5555::8888'], + [true, '1111:2222:3333:4444::'], + [true, '1111:2222:3333:4444::123.123.123.123'], + [true, '1111:2222:3333:4444::6666:123.123.123.123'], + [true, '1111:2222:3333:4444::6666:7777:8888'], + [true, '1111:2222:3333:4444::7777:8888'], + [true, '1111:2222:3333:4444::8888'], + [true, '1111:2222:3333::'], + [true, '1111:2222:3333::123.123.123.123'], + [true, '1111:2222:3333::5555:6666:123.123.123.123'], + [true, '1111:2222:3333::5555:6666:7777:8888'], + [true, '1111:2222:3333::6666:123.123.123.123'], + [true, '1111:2222:3333::6666:7777:8888'], + [true, '1111:2222:3333::7777:8888'], + [true, '1111:2222:3333::8888'], + [true, '1111:2222::'], + [true, '1111:2222::123.123.123.123'], + [true, '1111:2222::4444:5555:6666:123.123.123.123'], + [true, '1111:2222::4444:5555:6666:7777:8888'], + [true, '1111:2222::5555:6666:123.123.123.123'], + [true, '1111:2222::5555:6666:7777:8888'], + [true, '1111:2222::6666:123.123.123.123'], + [true, '1111:2222::6666:7777:8888'], + [true, '1111:2222::7777:8888'], + [true, '1111:2222::8888'], + [true, '1111::'], + [true, '1111::123.123.123.123'], + [true, '1111::3333:4444:5555:6666:123.123.123.123'], + [true, '1111::3333:4444:5555:6666:7777:8888'], + [true, '1111::4444:5555:6666:123.123.123.123'], + [true, '1111::4444:5555:6666:7777:8888'], + [true, '1111::5555:6666:123.123.123.123'], + [true, '1111::5555:6666:7777:8888'], + [true, '1111::6666:123.123.123.123'], + [true, '1111::6666:7777:8888'], + [true, '1111::7777:8888'], + [true, '1111::8888'], + [true, '123.23.34.2'], + [true, '172.26.168.134'], + [true, '192.168.0.0'], + [true, '192.168.128.255'], + [true, '1:2:3:4:5:6:1.2.3.4'], + [true, '1:2:3:4:5:6:7:8'], + [true, '1:2:3:4:5:6::'], + [true, '1:2:3:4:5:6::8'], + [true, '1:2:3:4:5::'], + [true, '1:2:3:4:5::1.2.3.4'], + [true, '1:2:3:4:5::7:8'], + [true, '1:2:3:4:5::8'], + [true, '1:2:3:4::'], + [true, '1:2:3:4::1.2.3.4'], + [true, '1:2:3:4::5:1.2.3.4'], + [true, '1:2:3:4::7:8'], + [true, '1:2:3:4::8'], + [true, '1:2:3::'], + [true, '1:2:3::1.2.3.4'], + [true, '1:2:3::5:1.2.3.4'], + [true, '1:2:3::7:8'], + [true, '1:2:3::8'], + [true, '1:2::'], + [true, '1:2::1.2.3.4'], + [true, '1:2::5:1.2.3.4'], + [true, '1:2::7:8'], + [true, '1:2::8'], + [true, '1::'], + [true, '1::1.2.3.4'], + [true, '1::2:3'], + [true, '1::2:3:4'], + [true, '1::2:3:4:5'], + [true, '1::2:3:4:5:6'], + [true, '1::2:3:4:5:6:7'], + [true, '1::5:1.2.3.4'], + [true, '1::5:11.22.33.44'], + [true, '1::7:8'], + [true, '1::8'], + [true, '2001:0000:1234:0000:0000:C1C0:ABCD:0876'], + [true, '2001:0:1234::C1C0:ABCD:876'], + [true, '2001:0db8:0000:0000:0000:0000:1428:57ab'], + [true, '2001:0db8:0000:0000:0000::1428:57ab'], + [true, '2001:0db8:0:0:0:0:1428:57ab'], + [true, '2001:0db8:0:0::1428:57ab'], + [true, '2001:0db8:1234:0000:0000:0000:0000:0000'], + [true, '2001:0db8:1234::'], + [true, '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff'], + [true, '2001:0db8:85a3:0000:0000:8a2e:0370:7334'], + [true, '2001:0db8::1428:57ab'], + [true, '2001:2:3:4:5:6:7:134'], + [true, '2001:DB8:0:0:8:800:200C:417A'], + [true, '2001:DB8::8:800:200C:417A'], + [true, '2001:db8:85a3:0:0:8a2e:370:7334'], + [true, '2001:db8:85a3::8a2e:370:7334'], + [true, '2001:db8::'], + [true, '2001:db8::1428:57ab'], + [true, '2001:db8:a::123'], + [true, '2002::'], + [true, '2::10'], + [true, '3ffe:0b00:0000:0000:0001:0000:0000:000a'], + [true, '3ffe:b00::1:0:0:a'], + [true, '::'], + [true, '::0'], + [true, '::0:0'], + [true, '::0:0:0'], + [true, '::0:0:0:0'], + [true, '::0:0:0:0:0'], + [true, '::0:0:0:0:0:0'], + [true, '::0:0:0:0:0:0:0'], + [true, '::0:a:b:c:d:e:f'], + [true, '::1'], + [true, '::123.123.123.123'], + [true, '::13.1.68.3'], + [true, '::2222:3333:4444:5555:6666:123.123.123.123'], + [true, '::2222:3333:4444:5555:6666:7777:8888'], + [true, '::2:3'], + [true, '::2:3:4'], + [true, '::2:3:4:5'], + [true, '::2:3:4:5:6'], + [true, '::2:3:4:5:6:7'], + [true, '::2:3:4:5:6:7:8'], + [true, '::3333:4444:5555:6666:7777:8888'], + [true, '::4444:5555:6666:123.123.123.123'], + [true, '::4444:5555:6666:7777:8888'], + [true, '::5555:6666:123.123.123.123'], + [true, '::5555:6666:7777:8888'], + [true, '::6666:123.123.123.123'], + [true, '::6666:7777:8888'], + [true, '::7777:8888'], + [true, '::8'], + [true, '::8888'], + [true, '::FFFF:129.144.52.38'], + [true, '::ffff:0:0'], + [true, '::ffff:0c22:384e'], + [true, '::ffff:12.34.56.78'], + [true, '::ffff:192.0.2.128'], + [true, '::ffff:192.168.1.1'], + [true, '::ffff:192.168.1.26'], + [true, '::ffff:c000:280'], + [true, 'FF01:0:0:0:0:0:0:101'], + [true, 'FF01::101'], + [true, 'FF02:0000:0000:0000:0000:0000:0000:0001'], + [true, 'FF02::1'], + [true, 'a:b:c:d:e:f:0::'], + [true, 'fe80:0000:0000:0000:0204:61ff:fe9d:f156'], + [true, 'fe80:0:0:0:204:61ff:254.157.241.86'], + [true, 'fe80:0:0:0:204:61ff:fe9d:f156'], + [true, 'fe80::'], + [true, 'fe80::1'], + [true, 'fe80::204:61ff:254.157.241.86'], + [true, 'fe80::204:61ff:fe9d:f156'], + [true, 'fe80::217:f2ff:254.7.237.98'], + [true, 'fe80::217:f2ff:fe07:ed62'], + [true, 'ff02::1'], +] + +describe('get_ipany_re', function () { + const net_utils = require('../index') + + it('IPv6, Prefix', function (done) { + // for x-*-ip headers + // it must fail as of not valide + assert.ok(!net.isIPv6('IPv6:2001:db8:85a3::8a2e:370:7334')) + // must okay! + assert.ok(net.isIPv6('2001:db8:85a3::8a2e:370:7334')) + done() + }) + + it('IP fixtures check', function (done) { + for (const i in ip_fixtures) { + const match = net_utils.get_ipany_re('^', '$').test(ip_fixtures[i][1]) + // console.log('IP:', `'${ip_fixtures[i][1]}'` , 'Expected:', ip_fixtures[i][0] , 'Match:' , match); + assert.ok( + match === ip_fixtures[i][0], + `${ip_fixtures[i][1]} - Expected: ${ip_fixtures[i][0]} - Match: ${match}`, + ) + } + done() + }) + + it('IPv4, bare', function (done) { + // for x-*-ip headers + const match = net_utils.get_ipany_re().exec('127.0.0.1') + assert.equal(match[1], '127.0.0.1') + assert.equal(match.length, 2) + done() + }) + + it('IPv4, Received header, parens', function (done) { + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(]', + '[\\]\\)]', + ) + const match = received_re.exec( + 'Received: from unknown (HELO mail.theartfarm.com) (127.0.0.30) by mail.theartfarm.com with SMTP; 5 Sep 2015 14:29:00 -0000', + ) + assert.equal(match[1], '127.0.0.30') + assert.equal(match.length, 2) + done() + }) + + it('IPv4, Received header, bracketed, expedia', function (done) { + const received_header = + 'Received: from mta2.expediamail.com (mta2.expediamail.com [66.231.89.19]) by mail.theartfarm.com (Haraka/2.6.2-toaster) with ESMTPS id C669CF18-1C1C-484C-8A5B-A89088B048CB.1 envelope-from (version=TLSv1/SSLv3 cipher=AES256-SHA verify=NO); Sat, 05 Sep 2015 07:28:57 -0700' + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(]', + '[\\]\\)]', + ) + const match = received_re.exec(received_header) + assert.equal(match[1], '66.231.89.19') + assert.equal(match.length, 2) + done() + }) + + it('IPv4, Received header, bracketed, github', function (done) { + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(]', + '[\\]\\)]', + ) + const match = received_re.exec( + 'Received: from github-smtp2a-ext-cp1-prd.iad.github.net (github-smtp2-ext5.iad.github.net [192.30.252.196])', + ) + assert.equal(match[1], '192.30.252.196') + assert.equal(match.length, 2) + done() + }) + + it('IPv6, Received header, bracketed', function (done) { + const received_header = + 'Received: from ?IPv6:2601:184:c001:5cf7:a53f:baf7:aaf3:bce7? ([2601:184:c001:5cf7:a53f:baf7:aaf3:bce7])' + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(]', + '[\\]\\)]', + ) + const match = received_re.exec(received_header) + assert.equal(match[1], '2601:184:c001:5cf7:a53f:baf7:aaf3:bce7') + assert.equal(match.length, 2) + done() + }) + + it('IPv6, Received header, bracketed, IPv6 prefix', function (done) { + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(](?:IPv6:)?', + '[\\]\\)]', + ) + const match = received_re.exec( + 'Received: from hub.freebsd.org (hub.freebsd.org [IPv6:2001:1900:2254:206c::16:88])', + ) + assert.equal(match[1], '2001:1900:2254:206c::16:88') + assert.equal(match.length, 2) + done() + }) + + it('IPv6, folded Received header, bracketed, IPv6 prefix', function (done) { + // note the use of [\s\S], '.' doesn't match newlines in JS regexp + const received_re = net_utils.get_ipany_re( + '^Received:[\\s\\S]*?[\\[\\(](?:IPv6:)?', + '[\\]\\)]', + ) + const match = received_re.exec( + 'Received: from freefall.freebsd.org (freefall.freebsd.org\r\n [IPv6:2001:1900:2254:206c::16:87])', + ) + if (match) { + assert.equal(match[1], '2001:1900:2254:206c::16:87') + assert.equal(match.length, 2) + } + done() + }) + + it('IPv6, Received header, bracketed, IPv6 prefix, localhost compressed', function (done) { + const received_re = net_utils.get_ipany_re( + '^Received:.*?[\\[\\(](?:IPv6:)?', + '[\\]\\)]', + ) + const match = received_re.exec( + 'Received: from ietfa.amsl.com (localhost [IPv6:::1])', + ) + assert.equal(match[1], '::1') + assert.equal(match.length, 2) + done() + }) + + it('IPv6 bogus', function (done) { + const is_bogus = net_utils.ipv6_bogus('::192.41.13.251') // From https://github.com/haraka/Haraka/issues/2763 + assert.equal(is_bogus, true) + done() + }) +}) diff --git a/test/get_mx.js b/test/get_mx.js new file mode 100644 index 0000000..b08cbd4 --- /dev/null +++ b/test/get_mx.js @@ -0,0 +1,182 @@ +const assert = require('assert') + +describe('get_mx', function () { + this.timeout(12000) + + beforeEach(function (done) { + this.net_utils = require('../index') + done() + }) + + const validCases = { + 'tnpi.net': 'mail.theartfarm.com', + 'matt@tnpi.net': 'mail.theartfarm.com', + 'matt.simerson@gmail.com': /google.com/, + 'example.com': '', + 'no-mx.haraka.tnpi.net': '192.0.99.5', + 'bad-mx.haraka.tnpi.net': /99/, + 'über.haraka.tnpi.net': 'no-mx.haraka.tnpi.net', + } + + function checkValid(c, mxlist) { + try { + if ('string' === typeof c) { + assert.equal(mxlist[0].exchange, c) + } else { + assert.ok(c.test(mxlist[0].exchange)) + } + } catch (err) { + console.error(err) + } + } + + for (const c in validCases) { + it(`gets MX records for ${c}`, function (done) { + this.timeout(12000) + this.net_utils.get_mx(c, (err, mxlist) => { + if (err) console.error(err) + assert.ifError(err) + checkValid(validCases[c], mxlist) + done() + }) + }) + + it(`awaits MX records for ${c}`, async function () { + this.timeout(12000) + const mxlist = await this.net_utils.get_mx(c) + checkValid(validCases[c], mxlist) + }) + } + + // macOS: ENODATA, win: ENOTOUND, ubuntu: ESERVFAIL + const invalidCases = { + invalid: /queryMx (ENODATA|ENOTFOUND|ESERVFAIL) invalid/, + 'gmail.xn--com-0da': /(ENOTFOUND|Cannot convert name to ASCII)/, + 'non-exist.haraka.tnpi.net': /ignore/, + 'haraka.non-exist': /ignore/, + } + + function checkInvalid(expected, actual) { + if ('string' === typeof expected) { + assert.strictEqual(actual, expected) + } else { + assert.equal(expected.test(actual), true) + } + } + + for (const c in invalidCases) { + it(`cb does not crash on invalid name: ${c}`, function () { + this.net_utils.get_mx(c, (err, mxlist) => { + if (err) checkInvalid(invalidCases[c], err.message) + assert.equal(mxlist.length, 0) + }) + }) + + it(`async does not crash on invalid name: ${c}`, async function () { + try { + const mxlist = await this.net_utils.get_mx(c) + assert.equal(mxlist.length, 0) + } catch (err) { + checkInvalid(invalidCases[c], err.message) + } + }) + } + + describe('resolve_mx_hosts', function () { + this.timeout(12000) + + beforeEach((done) => { + this.net_utils = require('../index') + done() + }) + + const expectedResolvedMx = [ + { + exchange: '2605:ae00:329::14', + priority: 10, + from_dns: 'mail.theartfarm.com', + }, + { + exchange: '66.128.51.165', + priority: 10, + from_dns: 'mail.theartfarm.com', + }, + ] + + it('resolves mx hosts to IPs, tnpi.net', async () => { + const r = await this.net_utils.resolve_mx_hosts([ + { exchange: 'mail.theartfarm.com', priority: 10, from_dns: 'tnpi.net' }, + ]) + assert.deepEqual(r, expectedResolvedMx) + }) + + it('resolves mx hosts to IPs, gmail.com', async () => { + const mxes = await this.net_utils.get_mx('gmail.com') + assert.equal(mxes.length, 5) + const r = await this.net_utils.resolve_mx_hosts(mxes) + assert.equal(r.length, 10) + }) + + it('returns IPs as is', async () => { + const r = await this.net_utils.resolve_mx_hosts(expectedResolvedMx) + assert.deepEqual(r, expectedResolvedMx) + }) + + it('returns sockets as-is', async () => { + const r = await this.net_utils.resolve_mx_hosts([ + { path: '/var/run/sock' }, + ]) + assert.deepEqual(r, [{ path: '/var/run/sock' }]) + }) + + it('resolve_mx_hosts, gmail.com', async () => { + const mxes = await this.net_utils.get_mx('gmail.com') + const r = await this.net_utils.resolve_mx_hosts(mxes) + assert.equal(r.length, 10) + }) + + it('resolve_mx_hosts, yahoo.com', async () => { + const mxes = await this.net_utils.get_mx('yahoo.com') + const r = await this.net_utils.resolve_mx_hosts([mxes[0]]) + assert.equal(r.length, 8) + }) + }) + + describe('get_implicit_mx', function () { + this.timeout(5000) + + beforeEach(function (done) { + this.net_utils = require('../index') + done() + }) + + it('harakamail.com', async function () { + const mf = await this.net_utils.get_implicit_mx('harakamail.com') + assert.equal(mf.length, 1) + }) + + it('mx.theartfarm.com', async function () { + const mf = await this.net_utils.get_implicit_mx('mx.theartfarm.com') + assert.equal(mf.length, 0) + }) + + it('resolve-fail-definitive.josef-froehle.de', async function () { + const mf = await this.net_utils.get_implicit_mx( + 'resolve-fail-definitive.josef-froehle.de', + ) + assert.equal(mf.length, 0) + }) + it('resolve-fail-a.josef-froehle.de', async function () { + const mf = await this.net_utils.get_implicit_mx( + 'resolve-fail-a.josef-froehle.de', + ) + assert.equal(mf.length, 1) + }) + it('resolve-fail-aaaa.josef-froehle.de', async function () { + const mf = await this.net_utils.get_implicit_mx( + 'resolve-fail-aaaa.josef-froehle.de', + ) + assert.equal(mf.length, 0) + }) + }) +}) diff --git a/test/get_public_ip.js b/test/get_public_ip.js new file mode 100644 index 0000000..d61ed30 --- /dev/null +++ b/test/get_public_ip.js @@ -0,0 +1,79 @@ +const assert = require('node:assert') +const path = require('node:path') + +function has_stun() { + try { + require('@msimerson/stun') + } catch (e) { + return false + } + return true +} + +beforeEach(function (done) { + this.net_utils = require('../lib/get_public_ip') + this.net_utils.config = this.net_utils.config.module_config( + path.resolve('test'), + ) + done() +}) + +describe('get_public_ip', function () { + it('is accessible via main nu', function () { + const nu = require('../index') + assert.equal(typeof nu.get_public_ip, 'function') + assert.equal(typeof nu.get_public_ip_async, 'function') + }) + + it('cached', function (done) { + this.net_utils.public_ip = '1.1.1.1' + this.net_utils.get_public_ip((err, ip) => { + assert.equal(null, err) + assert.equal('1.1.1.1', ip) + done() + }) + }) + + it('normal', function (done) { + this.net_utils.public_ip = undefined + this.net_utils.get_public_ip((err, ip) => { + if (has_stun()) { + if (err) { + console.error(err) + } else { + console.log(`stun success: ${ip}`) + assert.equal(null, err) + assert.ok(ip, ip) + } + } else { + console.log('stun skipped') + } + done() + }) + }) + + describe('get_public_ip_async', function () { + it('cached', async function () { + this.net_utils.public_ip = '1.1.1.1' + const ip = await this.net_utils.get_public_ip() + assert.equal('1.1.1.1', ip) + }) + + it('normal', async function () { + this.net_utils.public_ip = undefined + + if (!has_stun()) { + console.log('stun skipped') + return + } + + try { + const ip = await this.net_utils.get_public_ip() + console.log(`stun success: ${ip}`) + assert.ok(ip, ip) + } catch (e) { + console.error(e) + } + }) + }) +}) diff --git a/test/harakaMx.js b/test/harakaMx.js new file mode 100644 index 0000000..3a338d5 --- /dev/null +++ b/test/harakaMx.js @@ -0,0 +1,191 @@ +const assert = require('assert') + +process.env.NODE_ENV = 'test' + +describe('HarakaMx', () => { + beforeEach(function (done) { + this.nu = require('../index') + done() + }) + + describe('fromObject', () => { + it('accepts an object', function () { + assert.deepEqual( + new this.nu.HarakaMx({ + from_dns: 'example.com', + exchange: '.', + priority: 0, + }), + { from_dns: 'example.com', exchange: '.', priority: 0 }, + ) + }) + + it('sets default priority to 0', function () { + assert.deepEqual(new this.nu.HarakaMx({ exchange: '.' }), { + exchange: '.', + priority: 0, + }) + }) + + it('if optional domain provided, sets from_dns', function () { + assert.deepEqual(new this.nu.HarakaMx({ exchange: '.' }, 'example.com'), { + from_dns: 'example.com', + exchange: '.', + priority: 0, + }) + }) + }) + + describe('fromString', function () { + it('parses a hostname', function () { + assert.deepEqual(new this.nu.HarakaMx('mail.example.com'), { + exchange: 'mail.example.com', + priority: 0, + }) + }) + + it('parses a hostname:port', function () { + assert.deepEqual(new this.nu.HarakaMx('mail.example.com:25'), { + exchange: 'mail.example.com', + port: 25, + priority: 0, + }) + }) + + it('parses an IPv4', function () { + assert.deepEqual(new this.nu.HarakaMx('192.0.2.1'), { + exchange: '192.0.2.1', + priority: 0, + }) + }) + + it('parses an IPv4:port', function () { + assert.deepEqual(new this.nu.HarakaMx('192.0.2.1:25'), { + exchange: '192.0.2.1', + port: 25, + priority: 0, + }) + }) + + it('parses an IPv6', function () { + assert.deepEqual(new this.nu.HarakaMx('2001:db8::1'), { + exchange: '2001:db8::1', + priority: 0, + }) + }) + + it('parses an IPv6:port', function () { + assert.deepEqual(new this.nu.HarakaMx('2001:db8::1:25'), { + exchange: '2001:db8::1', + port: 25, + priority: 0, + }) + }) + + it('parses an [IPv6]:port', function () { + assert.deepEqual(new this.nu.HarakaMx('[2001:db8::1]:25'), { + exchange: '2001:db8::1', + port: 25, + priority: 0, + }) + }) + }) + + describe('fromUrl', function () { + it('parses simple URIs', function () { + assert.deepEqual(new this.nu.HarakaMx('smtp://192.0.2.2'), { + exchange: '192.0.2.2', + priority: 0, + }) + + assert.deepEqual(new this.nu.HarakaMx('smtp://[2001:db8::1]:25'), { + exchange: '[2001:db8::1]', + port: 25, + priority: 0, + }) + }) + + it('parses more complex URIs', function () { + assert.deepEqual( + new this.nu.HarakaMx('smtp://authUser:sekretPass@[2001:db8::1]'), + { + exchange: '[2001:db8::1]', + priority: 0, + auth_pass: 'sekretPass', + auth_user: 'authUser', + }, + ) + + assert.deepEqual( + new this.nu.HarakaMx('lmtp://authUser:sekretPass@[2001:db8::1]:25'), + { + exchange: '[2001:db8::1]', + port: 25, + priority: 0, + using_lmtp: true, + auth_pass: 'sekretPass', + auth_user: 'authUser', + }, + ) + }) + }) + + const testCases = [ + { in: { exchange: '.' }, url: 'smtp://.', str: 'MX 0 smtp://.' }, + { + in: { + from_dns: 'example.com', + exchange: '.', + priority: 10, + }, + url: 'smtp://.', + str: 'MX 10 smtp://. (via DNS)', + }, + { + in: 'smtp://au:ap@192.0.2.3:25', + url: 'smtp://au:****@192.0.2.3:25', + str: 'MX 0 smtp://au:****@192.0.2.3:25', + }, + { + in: 'smtp://au:ap@192.0.2.3:465', + url: 'smtp://au:****@192.0.2.3:465', + str: 'MX 0 smtp://au:****@192.0.2.3:465', + }, + { + in: 'smtp://[2001:db8::1]:25', + url: 'smtp://[2001:db8::1]:25', + str: 'MX 0 smtp://[2001:db8::1]:25', + }, + { + in: { path: '/var/run/sock' }, + url: 'file:///var/run/sock', + str: 'MX 0 file:///var/run/sock', + }, + ] + + describe('toUrl', function () { + for (const c of testCases) { + it(`${JSON.stringify(c.in)} -> ${c.url}`, function () { + assert.equal(new this.nu.HarakaMx(c.in).toUrl(), c.url) + }) + } + }) + + describe('toString', function () { + for (const c of testCases) { + it(`${JSON.stringify(c.in)} -> ${c.str}`, function () { + assert.equal(new this.nu.HarakaMx(c.in).toString(), c.str) + }) + } + }) + + it('is exported from nu', function () { + const nu = require('../index') + assert.equal(typeof nu.HarakaMx, 'function') + }) + + it('directly loadable', function () { + const hMx = require('../lib/HarakaMx') + assert.equal(typeof hMx, 'function') + }) +}) diff --git a/test/net_utils.js b/test/net_utils.js index a547a83..8222bb3 100644 --- a/test/net_utils.js +++ b/test/net_utils.js @@ -1,7 +1,7 @@ -const assert = require('assert') -const net = require('net') -const os = require('os') -const path = require('path') +const assert = require('node:assert') +const EventEmitter = require('node:events') +const os = require('node:os') +const path = require('node:path') require('haraka-config').watch_files = false const net_utils = require('../index') @@ -240,97 +240,6 @@ describe('is_private_ip', function () { }) }) -describe('get_public_ip', function () { - beforeEach(function (done) { - this.net_utils = require('../index') - this.net_utils.config = this.net_utils.config.module_config( - path.resolve('test'), - ) - done() - }) - - function has_stun() { - try { - require('stun') - } catch (e) { - return false - } - return true - } - - it('cached', function (done) { - this.net_utils.public_ip = '1.1.1.1' - const cb = function (err, ip) { - assert.equal(null, err) - assert.equal('1.1.1.1', ip) - done() - } - this.net_utils.get_public_ip(cb) - }) - - it('normal', function (done) { - this.net_utils.public_ip = undefined - const cb = function (err, ip) { - // console.log(`ip: ${ip}`); - // console.log(`err: ${err}`); - if (has_stun()) { - if (err) { - console.log(err) - } else { - console.log(`stun success: ${ip}`) - assert.equal(null, err) - assert.ok(ip, ip) - } - } else { - console.log('stun skipped') - } - done() - } - this.net_utils.get_public_ip(cb) - }) -}) - -describe('get_public_ip_async', function () { - beforeEach(() => { - this.net_utils = require('../index') - this.net_utils.config = this.net_utils.config.module_config( - path.resolve('test'), - ) - }) - - function has_stun() { - try { - require('stun') - } catch (e) { - return false - } - return true - } - - it('cached', async () => { - this.net_utils.public_ip = '1.1.1.1' - const ip = await this.net_utils.get_public_ip() - assert.equal('1.1.1.1', ip) - }) - - it('normal', async () => { - this.net_utils.public_ip = undefined - - if (!has_stun()) { - console.log('stun skipped') - return - } - - try { - const ip = await this.net_utils.get_public_ip() - console.log(`stun success: ${ip}`) - assert.ok(ip, ip) - } catch (e) { - console.error(e) - } - }) -}) - describe('octets_in_string', function () { it('c-24-18-98-14.hsd1.wa.comcast.net', function (done) { const str = 'c-24-18-98-14.hsd1.wa.comcast.net' @@ -471,629 +380,6 @@ describe('is_local_ipv6', function () { }) }) -const ip_fixtures = [ - [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876 '], - [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0'], - [false, ' 2001:0000:1234:0000:0000:C1C0:ABCD:0876'], - [false, ' 2001:0:1234::C1C0:ABCD:876 '], - [false, ' 2001:0:1234::C1C0:ABCD:876'], - [false, ''], - [false, "':10.0.0.1"], - [false, '---'], - [false, '02001:0000:1234:0000:0000:C1C0:ABCD:0876'], - [false, '1.2.3.4:1111:2222:3333:4444::5555'], - [false, '1.2.3.4:1111:2222:3333::5555'], - [false, '1.2.3.4:1111:2222::5555'], - [false, '1.2.3.4:1111::5555'], - [false, '1.2.3.4::'], - [false, '1.2.3.4::5555'], - [false, '1111'], - [false, '11112222:3333:4444:5555:6666:1.2.3.4'], - [false, '11112222:3333:4444:5555:6666:7777:8888'], - [false, '1111:'], - [false, '1111:1.2.3.4'], - [false, '1111:2222'], - [false, '1111:22223333:4444:5555:6666:1.2.3.4'], - [false, '1111:22223333:4444:5555:6666:7777:8888'], - [false, '1111:2222:'], - [false, '1111:2222:1.2.3.4'], - [false, '1111:2222:3333'], - [false, '1111:2222:33334444:5555:6666:1.2.3.4'], - [false, '1111:2222:33334444:5555:6666:7777:8888'], - [false, '1111:2222:3333:'], - [false, '1111:2222:3333:1.2.3.4'], - [false, '1111:2222:3333:4444'], - [false, '1111:2222:3333:44445555:6666:1.2.3.4'], - [false, '1111:2222:3333:44445555:6666:7777:8888'], - [false, '1111:2222:3333:4444:'], - [false, '1111:2222:3333:4444:1.2.3.4'], - [false, '1111:2222:3333:4444:5555'], - [false, '1111:2222:3333:4444:55556666:1.2.3.4'], - [false, '1111:2222:3333:4444:55556666:7777:8888'], - [false, '1111:2222:3333:4444:5555:'], - [false, '1111:2222:3333:4444:5555:1.2.3.4'], - [false, '1111:2222:3333:4444:5555:6666'], - [false, '1111:2222:3333:4444:5555:66661.2.3.4'], - [false, '1111:2222:3333:4444:5555:66667777:8888'], - [false, '1111:2222:3333:4444:5555:6666:'], - [false, '1111:2222:3333:4444:5555:6666:00.00.00.00'], - [false, '1111:2222:3333:4444:5555:6666:000.000.000.000'], - [false, '1111:2222:3333:4444:5555:6666:1.2.3.4.5'], - [false, '1111:2222:3333:4444:5555:6666:255.255.255255'], - [false, '1111:2222:3333:4444:5555:6666:255.255255.255'], - [false, '1111:2222:3333:4444:5555:6666:255255.255.255'], - [false, '1111:2222:3333:4444:5555:6666:256.256.256.256'], - [false, '1111:2222:3333:4444:5555:6666:7777'], - [false, '1111:2222:3333:4444:5555:6666:77778888'], - [false, '1111:2222:3333:4444:5555:6666:7777:'], - [false, '1111:2222:3333:4444:5555:6666:7777:1.2.3.4'], - [false, '1111:2222:3333:4444:5555:6666:7777:8888:'], - [false, '1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4'], - [false, '1111:2222:3333:4444:5555:6666:7777:8888:9999'], - [false, '1111:2222:3333:4444:5555:6666:7777:8888::'], - [false, '1111:2222:3333:4444:5555:6666:7777:::'], - [false, '1111:2222:3333:4444:5555:6666::1.2.3.4'], - [false, '1111:2222:3333:4444:5555:6666::8888:'], - [false, '1111:2222:3333:4444:5555:6666:::'], - [false, '1111:2222:3333:4444:5555:6666:::8888'], - [false, '1111:2222:3333:4444:5555::7777:8888:'], - [false, '1111:2222:3333:4444:5555::7777::'], - [false, '1111:2222:3333:4444:5555::8888:'], - [false, '1111:2222:3333:4444:5555:::'], - [false, '1111:2222:3333:4444:5555:::1.2.3.4'], - [false, '1111:2222:3333:4444:5555:::7777:8888'], - [false, '1111:2222:3333:4444::5555:'], - [false, '1111:2222:3333:4444::6666:7777:8888:'], - [false, '1111:2222:3333:4444::6666:7777::'], - [false, '1111:2222:3333:4444::6666::8888'], - [false, '1111:2222:3333:4444::7777:8888:'], - [false, '1111:2222:3333:4444::8888:'], - [false, '1111:2222:3333:4444:::'], - [false, '1111:2222:3333:4444:::6666:1.2.3.4'], - [false, '1111:2222:3333:4444:::6666:7777:8888'], - [false, '1111:2222:3333::5555:'], - [false, '1111:2222:3333::5555:6666:7777:8888:'], - [false, '1111:2222:3333::5555:6666:7777::'], - [false, '1111:2222:3333::5555:6666::8888'], - [false, '1111:2222:3333::5555::1.2.3.4'], - [false, '1111:2222:3333::5555::7777:8888'], - [false, '1111:2222:3333::6666:7777:8888:'], - [false, '1111:2222:3333::7777:8888:'], - [false, '1111:2222:3333::8888:'], - [false, '1111:2222:3333:::'], - [false, '1111:2222:3333:::5555:6666:1.2.3.4'], - [false, '1111:2222:3333:::5555:6666:7777:8888'], - [false, '1111:2222::4444:5555:6666:7777:8888:'], - [false, '1111:2222::4444:5555:6666:7777::'], - [false, '1111:2222::4444:5555:6666::8888'], - [false, '1111:2222::4444:5555::1.2.3.4'], - [false, '1111:2222::4444:5555::7777:8888'], - [false, '1111:2222::4444::6666:1.2.3.4'], - [false, '1111:2222::4444::6666:7777:8888'], - [false, '1111:2222::5555:'], - [false, '1111:2222::5555:6666:7777:8888:'], - [false, '1111:2222::6666:7777:8888:'], - [false, '1111:2222::7777:8888:'], - [false, '1111:2222::8888:'], - [false, '1111:2222:::'], - [false, '1111:2222:::4444:5555:6666:1.2.3.4'], - [false, '1111:2222:::4444:5555:6666:7777:8888'], - [false, '1111::3333:4444:5555:6666:7777:8888:'], - [false, '1111::3333:4444:5555:6666:7777::'], - [false, '1111::3333:4444:5555:6666::8888'], - [false, '1111::3333:4444:5555::1.2.3.4'], - [false, '1111::3333:4444:5555::7777:8888'], - [false, '1111::3333:4444::6666:1.2.3.4'], - [false, '1111::3333:4444::6666:7777:8888'], - [false, '1111::3333::5555:6666:1.2.3.4'], - [false, '1111::3333::5555:6666:7777:8888'], - [false, '1111::4444:5555:6666:7777:8888:'], - [false, '1111::5555:'], - [false, '1111::5555:6666:7777:8888:'], - [false, '1111::6666:7777:8888:'], - [false, '1111::7777:8888:'], - [false, '1111::8888:'], - [false, '1111:::'], - [false, '1111:::3333:4444:5555:6666:1.2.3.4'], - [false, '1111:::3333:4444:5555:6666:7777:8888'], - [false, '123'], - [false, '12345::6:7:8'], - [false, '192.168.0.256'], - [false, '192.168.256.0'], - [false, '1:2:3:4:5:6:7:8:9'], - [false, '1:2:3::4:5:6:7:8:9'], - [false, '1:2:3::4:5::7:8'], - [false, '1::1.2.256.4'], - [false, '1::1.2.3.256'], - [false, '1::1.2.3.300'], - [false, '1::1.2.3.900'], - [false, '1::1.2.300.4'], - [false, '1::1.2.900.4'], - [false, '1::1.256.3.4'], - [false, '1::1.300.3.4'], - [false, '1::1.900.3.4'], - [false, '1::256.2.3.4'], - [false, '1::260.2.3.4'], - [false, '1::2::3'], - [false, '1::300.2.3.4'], - [false, '1::300.300.300.300'], - [false, '1::3000.30.30.30'], - [false, '1::400.2.3.4'], - [false, '1::5:1.2.256.4'], - [false, '1::5:1.2.3.256'], - [false, '1::5:1.2.3.300'], - [false, '1::5:1.2.3.900'], - [false, '1::5:1.2.300.4'], - [false, '1::5:1.2.900.4'], - [false, '1::5:1.256.3.4'], - [false, '1::5:1.300.3.4'], - [false, '1::5:1.900.3.4'], - [false, '1::5:256.2.3.4'], - [false, '1::5:260.2.3.4'], - [false, '1::5:300.2.3.4'], - [false, '1::5:300.300.300.300'], - [false, '1::5:3000.30.30.30'], - [false, '1::5:400.2.3.4'], - [false, '1::5:900.2.3.4'], - [false, '1::900.2.3.4'], - [false, '1:::3:4:5'], - [false, '2001:0000:1234: 0000:0000:C1C0:ABCD:0876'], - [false, '2001:0000:1234:0000:00001:C1C0:ABCD:0876'], - [false, '2001:0000:1234:0000:0000:C1C0:ABCD:0876 0'], - [false, '2001:1:1:1:1:1:255Z255X255Y255'], - [false, '2001::FFD3::57ab'], - [false, '2001:DB8:0:0:8:800:200C:417A:221'], - [false, '2001:db8:85a3::8a2e:37023:7334'], - [false, '2001:db8:85a3::8a2e:370k:7334'], - [false, '255.256.255.255'], - [false, '256.255.255.255'], - [false, '3ffe:0b00:0000:0001:0000:0000:000a'], - [false, '3ffe:b00::1::a'], - [false, ':'], - [false, ':1.2.3.4'], - [false, ':1111:2222:3333:4444:5555:6666:1.2.3.4'], - [false, ':1111:2222:3333:4444:5555:6666:7777:8888'], - [false, ':1111:2222:3333:4444:5555:6666:7777::'], - [false, ':1111:2222:3333:4444:5555:6666::'], - [false, ':1111:2222:3333:4444:5555:6666::8888'], - [false, ':1111:2222:3333:4444:5555::'], - [false, ':1111:2222:3333:4444:5555::1.2.3.4'], - [false, ':1111:2222:3333:4444:5555::7777:8888'], - [false, ':1111:2222:3333:4444:5555::8888'], - [false, ':1111:2222:3333:4444::'], - [false, ':1111:2222:3333:4444::1.2.3.4'], - [false, ':1111:2222:3333:4444::5555'], - [false, ':1111:2222:3333:4444::6666:1.2.3.4'], - [false, ':1111:2222:3333:4444::6666:7777:8888'], - [false, ':1111:2222:3333:4444::7777:8888'], - [false, ':1111:2222:3333:4444::8888'], - [false, ':1111:2222:3333::'], - [false, ':1111:2222:3333::1.2.3.4'], - [false, ':1111:2222:3333::5555'], - [false, ':1111:2222:3333::5555:6666:1.2.3.4'], - [false, ':1111:2222:3333::5555:6666:7777:8888'], - [false, ':1111:2222:3333::6666:1.2.3.4'], - [false, ':1111:2222:3333::6666:7777:8888'], - [false, ':1111:2222:3333::7777:8888'], - [false, ':1111:2222:3333::8888'], - [false, ':1111:2222::'], - [false, ':1111:2222::1.2.3.4'], - [false, ':1111:2222::4444:5555:6666:1.2.3.4'], - [false, ':1111:2222::4444:5555:6666:7777:8888'], - [false, ':1111:2222::5555'], - [false, ':1111:2222::5555:6666:1.2.3.4'], - [false, ':1111:2222::5555:6666:7777:8888'], - [false, ':1111:2222::6666:1.2.3.4'], - [false, ':1111:2222::6666:7777:8888'], - [false, ':1111:2222::7777:8888'], - [false, ':1111:2222::8888'], - [false, ':1111::'], - [false, ':1111::1.2.3.4'], - [false, ':1111::3333:4444:5555:6666:1.2.3.4'], - [false, ':1111::3333:4444:5555:6666:7777:8888'], - [false, ':1111::4444:5555:6666:1.2.3.4'], - [false, ':1111::4444:5555:6666:7777:8888'], - [false, ':1111::5555'], - [false, ':1111::5555:6666:1.2.3.4'], - [false, ':1111::5555:6666:7777:8888'], - [false, ':1111::6666:1.2.3.4'], - [false, ':1111::6666:7777:8888'], - [false, ':1111::7777:8888'], - [false, ':1111::8888'], - [false, ':2222:3333:4444:5555:6666:1.2.3.4'], - [false, ':2222:3333:4444:5555:6666:7777:8888'], - [false, ':3333:4444:5555:6666:1.2.3.4'], - [false, ':3333:4444:5555:6666:7777:8888'], - [false, ':4444:5555:6666:1.2.3.4'], - [false, ':4444:5555:6666:7777:8888'], - [false, ':5555:6666:1.2.3.4'], - [false, ':5555:6666:7777:8888'], - [false, ':6666:1.2.3.4'], - [false, ':6666:7777:8888'], - [false, ':7777:8888'], - [false, ':8888'], - [false, '::.'], - [false, '::..'], - [false, '::...'], - [false, '::...4'], - [false, '::..3.'], - [false, '::..3.4'], - [false, '::.2..'], - [false, '::.2.3.'], - [false, '::.2.3.4'], - [false, '::1...'], - [false, '::1.2..'], - [false, '::1.2.256.4'], - [false, '::1.2.3.'], - [false, '::1.2.3.256'], - [false, '::1.2.3.300'], - [false, '::1.2.3.900'], - [false, '::1.2.300.4'], - [false, '::1.2.900.4'], - [false, '::1.256.3.4'], - [false, '::1.300.3.4'], - [false, '::1.900.3.4'], - [false, '::1111:2222:3333:4444:5555:6666::'], - [false, '::2222:3333:4444:5555:6666:7777:1.2.3.4'], - [false, '::2222:3333:4444:5555:6666:7777:8888:'], - [false, '::2222:3333:4444:5555:6666:7777:8888:9999'], - [false, '::2222:3333:4444:5555:7777:8888::'], - [false, '::2222:3333:4444:5555:7777::8888'], - [false, '::2222:3333:4444:5555::1.2.3.4'], - [false, '::2222:3333:4444:5555::7777:8888'], - [false, '::2222:3333:4444::6666:1.2.3.4'], - [false, '::2222:3333:4444::6666:7777:8888'], - [false, '::2222:3333::5555:6666:1.2.3.4'], - [false, '::2222:3333::5555:6666:7777:8888'], - [false, '::2222::4444:5555:6666:1.2.3.4'], - [false, '::2222::4444:5555:6666:7777:8888'], - [false, '::256.2.3.4'], - [false, '::260.2.3.4'], - [false, '::300.2.3.4'], - [false, '::300.300.300.300'], - [false, '::3000.30.30.30'], - [false, '::3333:4444:5555:6666:7777:8888:'], - [false, '::400.2.3.4'], - [false, '::4444:5555:6666:7777:8888:'], - [false, '::5555:'], - [false, '::5555:6666:7777:8888:'], - [false, '::6666:7777:8888:'], - [false, '::7777:8888:'], - [false, '::8888:'], - [false, '::900.2.3.4'], - [false, ':::'], - [false, ':::1.2.3.4'], - [false, ':::2222:3333:4444:5555:6666:1.2.3.4'], - [false, ':::2222:3333:4444:5555:6666:7777:8888'], - [false, ':::3333:4444:5555:6666:7777:8888'], - [false, ':::4444:5555:6666:1.2.3.4'], - [false, ':::4444:5555:6666:7777:8888'], - [false, ':::5555'], - [false, ':::5555:6666:1.2.3.4'], - [false, ':::5555:6666:7777:8888'], - [false, ':::6666:1.2.3.4'], - [false, ':::6666:7777:8888'], - [false, ':::7777:8888'], - [false, ':::8888'], - [false, '::ffff:192x168.1.26'], - [false, '::ffff:2.3.4'], - [false, '::ffff:257.1.2.3'], - [false, 'FF01::101::2'], - [false, 'FF02:0000:0000:0000:0000:0000:0000:0000:0001'], - [false, 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4'], - [false, 'XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX'], - [false, 'fe80:0000:0000:0000:0204:61ff:254.157.241.086'], - [false, 'fe80::4413:c8ae:2821:5852%10'], - [false, 'ldkfj'], - [false, 'mydomain.com'], - [false, 'test.mydomain.com'], - [true, '0000:0000:0000:0000:0000:0000:0000:0000'], - [true, '0000:0000:0000:0000:0000:0000:0000:0001'], - [true, '0:0:0:0:0:0:0:0'], - [true, '0:0:0:0:0:0:0:1'], - [true, '0:0:0:0:0:0:0::'], - [true, '0:0:0:0:0:0:13.1.68.3'], - [true, '0:0:0:0:0:0::'], - [true, '0:0:0:0:0::'], - [true, '0:0:0:0:0:FFFF:129.144.52.38'], - [true, '0:0:0:0::'], - [true, '0:0:0::'], - [true, '0:0::'], - [true, '0::'], - [true, '0:a:b:c:d:e:f::'], - [true, '1.2.3.4'], - [true, '1111:2222:3333:4444:5555:6666:123.123.123.123'], - [true, '1111:2222:3333:4444:5555:6666:7777:8888'], - [true, '1111:2222:3333:4444:5555:6666:7777::'], - [true, '1111:2222:3333:4444:5555:6666::'], - [true, '1111:2222:3333:4444:5555:6666::8888'], - [true, '1111:2222:3333:4444:5555::'], - [true, '1111:2222:3333:4444:5555::123.123.123.123'], - [true, '1111:2222:3333:4444:5555::7777:8888'], - [true, '1111:2222:3333:4444:5555::8888'], - [true, '1111:2222:3333:4444::'], - [true, '1111:2222:3333:4444::123.123.123.123'], - [true, '1111:2222:3333:4444::6666:123.123.123.123'], - [true, '1111:2222:3333:4444::6666:7777:8888'], - [true, '1111:2222:3333:4444::7777:8888'], - [true, '1111:2222:3333:4444::8888'], - [true, '1111:2222:3333::'], - [true, '1111:2222:3333::123.123.123.123'], - [true, '1111:2222:3333::5555:6666:123.123.123.123'], - [true, '1111:2222:3333::5555:6666:7777:8888'], - [true, '1111:2222:3333::6666:123.123.123.123'], - [true, '1111:2222:3333::6666:7777:8888'], - [true, '1111:2222:3333::7777:8888'], - [true, '1111:2222:3333::8888'], - [true, '1111:2222::'], - [true, '1111:2222::123.123.123.123'], - [true, '1111:2222::4444:5555:6666:123.123.123.123'], - [true, '1111:2222::4444:5555:6666:7777:8888'], - [true, '1111:2222::5555:6666:123.123.123.123'], - [true, '1111:2222::5555:6666:7777:8888'], - [true, '1111:2222::6666:123.123.123.123'], - [true, '1111:2222::6666:7777:8888'], - [true, '1111:2222::7777:8888'], - [true, '1111:2222::8888'], - [true, '1111::'], - [true, '1111::123.123.123.123'], - [true, '1111::3333:4444:5555:6666:123.123.123.123'], - [true, '1111::3333:4444:5555:6666:7777:8888'], - [true, '1111::4444:5555:6666:123.123.123.123'], - [true, '1111::4444:5555:6666:7777:8888'], - [true, '1111::5555:6666:123.123.123.123'], - [true, '1111::5555:6666:7777:8888'], - [true, '1111::6666:123.123.123.123'], - [true, '1111::6666:7777:8888'], - [true, '1111::7777:8888'], - [true, '1111::8888'], - [true, '123.23.34.2'], - [true, '172.26.168.134'], - [true, '192.168.0.0'], - [true, '192.168.128.255'], - [true, '1:2:3:4:5:6:1.2.3.4'], - [true, '1:2:3:4:5:6:7:8'], - [true, '1:2:3:4:5:6::'], - [true, '1:2:3:4:5:6::8'], - [true, '1:2:3:4:5::'], - [true, '1:2:3:4:5::1.2.3.4'], - [true, '1:2:3:4:5::7:8'], - [true, '1:2:3:4:5::8'], - [true, '1:2:3:4::'], - [true, '1:2:3:4::1.2.3.4'], - [true, '1:2:3:4::5:1.2.3.4'], - [true, '1:2:3:4::7:8'], - [true, '1:2:3:4::8'], - [true, '1:2:3::'], - [true, '1:2:3::1.2.3.4'], - [true, '1:2:3::5:1.2.3.4'], - [true, '1:2:3::7:8'], - [true, '1:2:3::8'], - [true, '1:2::'], - [true, '1:2::1.2.3.4'], - [true, '1:2::5:1.2.3.4'], - [true, '1:2::7:8'], - [true, '1:2::8'], - [true, '1::'], - [true, '1::1.2.3.4'], - [true, '1::2:3'], - [true, '1::2:3:4'], - [true, '1::2:3:4:5'], - [true, '1::2:3:4:5:6'], - [true, '1::2:3:4:5:6:7'], - [true, '1::5:1.2.3.4'], - [true, '1::5:11.22.33.44'], - [true, '1::7:8'], - [true, '1::8'], - [true, '2001:0000:1234:0000:0000:C1C0:ABCD:0876'], - [true, '2001:0:1234::C1C0:ABCD:876'], - [true, '2001:0db8:0000:0000:0000:0000:1428:57ab'], - [true, '2001:0db8:0000:0000:0000::1428:57ab'], - [true, '2001:0db8:0:0:0:0:1428:57ab'], - [true, '2001:0db8:0:0::1428:57ab'], - [true, '2001:0db8:1234:0000:0000:0000:0000:0000'], - [true, '2001:0db8:1234::'], - [true, '2001:0db8:1234:ffff:ffff:ffff:ffff:ffff'], - [true, '2001:0db8:85a3:0000:0000:8a2e:0370:7334'], - [true, '2001:0db8::1428:57ab'], - [true, '2001:2:3:4:5:6:7:134'], - [true, '2001:DB8:0:0:8:800:200C:417A'], - [true, '2001:DB8::8:800:200C:417A'], - [true, '2001:db8:85a3:0:0:8a2e:370:7334'], - [true, '2001:db8:85a3::8a2e:370:7334'], - [true, '2001:db8::'], - [true, '2001:db8::1428:57ab'], - [true, '2001:db8:a::123'], - [true, '2002::'], - [true, '2::10'], - [true, '3ffe:0b00:0000:0000:0001:0000:0000:000a'], - [true, '3ffe:b00::1:0:0:a'], - [true, '::'], - [true, '::0'], - [true, '::0:0'], - [true, '::0:0:0'], - [true, '::0:0:0:0'], - [true, '::0:0:0:0:0'], - [true, '::0:0:0:0:0:0'], - [true, '::0:0:0:0:0:0:0'], - [true, '::0:a:b:c:d:e:f'], - [true, '::1'], - [true, '::123.123.123.123'], - [true, '::13.1.68.3'], - [true, '::2222:3333:4444:5555:6666:123.123.123.123'], - [true, '::2222:3333:4444:5555:6666:7777:8888'], - [true, '::2:3'], - [true, '::2:3:4'], - [true, '::2:3:4:5'], - [true, '::2:3:4:5:6'], - [true, '::2:3:4:5:6:7'], - [true, '::2:3:4:5:6:7:8'], - [true, '::3333:4444:5555:6666:7777:8888'], - [true, '::4444:5555:6666:123.123.123.123'], - [true, '::4444:5555:6666:7777:8888'], - [true, '::5555:6666:123.123.123.123'], - [true, '::5555:6666:7777:8888'], - [true, '::6666:123.123.123.123'], - [true, '::6666:7777:8888'], - [true, '::7777:8888'], - [true, '::8'], - [true, '::8888'], - [true, '::FFFF:129.144.52.38'], - [true, '::ffff:0:0'], - [true, '::ffff:0c22:384e'], - [true, '::ffff:12.34.56.78'], - [true, '::ffff:192.0.2.128'], - [true, '::ffff:192.168.1.1'], - [true, '::ffff:192.168.1.26'], - [true, '::ffff:c000:280'], - [true, 'FF01:0:0:0:0:0:0:101'], - [true, 'FF01::101'], - [true, 'FF02:0000:0000:0000:0000:0000:0000:0001'], - [true, 'FF02::1'], - [true, 'a:b:c:d:e:f:0::'], - [true, 'fe80:0000:0000:0000:0204:61ff:fe9d:f156'], - [true, 'fe80:0:0:0:204:61ff:254.157.241.86'], - [true, 'fe80:0:0:0:204:61ff:fe9d:f156'], - [true, 'fe80::'], - [true, 'fe80::1'], - [true, 'fe80::204:61ff:254.157.241.86'], - [true, 'fe80::204:61ff:fe9d:f156'], - [true, 'fe80::217:f2ff:254.7.237.98'], - [true, 'fe80::217:f2ff:fe07:ed62'], - [true, 'ff02::1'], -] - -describe('get_ipany_re', function () { - it('IPv6, Prefix', function (done) { - // for x-*-ip headers - // it must fail as of not valide - assert.ok(!net.isIPv6('IPv6:2001:db8:85a3::8a2e:370:7334')) - // must okay! - assert.ok(net.isIPv6('2001:db8:85a3::8a2e:370:7334')) - done() - }) - - it('IP fixtures check', function (done) { - for (const i in ip_fixtures) { - const match = net_utils.get_ipany_re('^', '$').test(ip_fixtures[i][1]) - // console.log('IP:', `'${ip_fixtures[i][1]}'` , 'Expected:', ip_fixtures[i][0] , 'Match:' , match); - assert.ok( - match === ip_fixtures[i][0], - `${ip_fixtures[i][1]} - Expected: ${ip_fixtures[i][0]} - Match: ${match}`, - ) - } - done() - }) - - it('IPv4, bare', function (done) { - // for x-*-ip headers - const match = net_utils.get_ipany_re().exec('127.0.0.1') - assert.equal(match[1], '127.0.0.1') - assert.equal(match.length, 2) - done() - }) - - it('IPv4, Received header, parens', function (done) { - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(]', - '[\\]\\)]', - ) - const match = received_re.exec( - 'Received: from unknown (HELO mail.theartfarm.com) (127.0.0.30) by mail.theartfarm.com with SMTP; 5 Sep 2015 14:29:00 -0000', - ) - assert.equal(match[1], '127.0.0.30') - assert.equal(match.length, 2) - done() - }) - - it('IPv4, Received header, bracketed, expedia', function (done) { - const received_header = - 'Received: from mta2.expediamail.com (mta2.expediamail.com [66.231.89.19]) by mail.theartfarm.com (Haraka/2.6.2-toaster) with ESMTPS id C669CF18-1C1C-484C-8A5B-A89088B048CB.1 envelope-from (version=TLSv1/SSLv3 cipher=AES256-SHA verify=NO); Sat, 05 Sep 2015 07:28:57 -0700' - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(]', - '[\\]\\)]', - ) - const match = received_re.exec(received_header) - assert.equal(match[1], '66.231.89.19') - assert.equal(match.length, 2) - done() - }) - - it('IPv4, Received header, bracketed, github', function (done) { - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(]', - '[\\]\\)]', - ) - const match = received_re.exec( - 'Received: from github-smtp2a-ext-cp1-prd.iad.github.net (github-smtp2-ext5.iad.github.net [192.30.252.196])', - ) - assert.equal(match[1], '192.30.252.196') - assert.equal(match.length, 2) - done() - }) - - it('IPv6, Received header, bracketed', function (done) { - const received_header = - 'Received: from ?IPv6:2601:184:c001:5cf7:a53f:baf7:aaf3:bce7? ([2601:184:c001:5cf7:a53f:baf7:aaf3:bce7])' - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(]', - '[\\]\\)]', - ) - const match = received_re.exec(received_header) - assert.equal(match[1], '2601:184:c001:5cf7:a53f:baf7:aaf3:bce7') - assert.equal(match.length, 2) - done() - }) - - it('IPv6, Received header, bracketed, IPv6 prefix', function (done) { - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(](?:IPv6:)?', - '[\\]\\)]', - ) - const match = received_re.exec( - 'Received: from hub.freebsd.org (hub.freebsd.org [IPv6:2001:1900:2254:206c::16:88])', - ) - assert.equal(match[1], '2001:1900:2254:206c::16:88') - assert.equal(match.length, 2) - done() - }) - - it('IPv6, folded Received header, bracketed, IPv6 prefix', function (done) { - // note the use of [\s\S], '.' doesn't match newlines in JS regexp - const received_re = net_utils.get_ipany_re( - '^Received:[\\s\\S]*?[\\[\\(](?:IPv6:)?', - '[\\]\\)]', - ) - const match = received_re.exec( - 'Received: from freefall.freebsd.org (freefall.freebsd.org\r\n [IPv6:2001:1900:2254:206c::16:87])', - ) - if (match) { - assert.equal(match[1], '2001:1900:2254:206c::16:87') - assert.equal(match.length, 2) - } - done() - }) - - it('IPv6, Received header, bracketed, IPv6 prefix, localhost compressed', function (done) { - const received_re = net_utils.get_ipany_re( - '^Received:.*?[\\[\\(](?:IPv6:)?', - '[\\]\\)]', - ) - const match = received_re.exec( - 'Received: from ietfa.amsl.com (localhost [IPv6:::1])', - ) - assert.equal(match[1], '::1') - assert.equal(match.length, 2) - done() - }) - - it('IPv6 bogus', function (done) { - const is_bogus = net_utils.ipv6_bogus('::192.41.13.251') // From https://github.com/haraka/Haraka/issues/2763 - assert.equal(is_bogus, true) - done() - }) -}) - describe('get_ips_by_host', function () { const tests = { 'servedby.tnpi.net': [ @@ -1122,6 +408,7 @@ describe('get_ips_by_host', function () { }) it(`get_ips_by_host, promise, ${t}`, async function () { + this.timeout(5000) try { const res = await net_utils.get_ips_by_host(t) assert.deepEqual(res.sort(), tests[t].sort()) @@ -1249,143 +536,27 @@ describe('on_local_interface', function () { }) }) -describe('get_mx', function () { - this.timeout(12000) - +describe('add_line_processor', function () { beforeEach(function (done) { this.net_utils = require('../index') + this.net_utils.config = this.net_utils.config.module_config( + path.resolve('test'), + ) done() }) - const validCases = { - 'tnpi.net': 'mail.theartfarm.com', - 'matt@tnpi.net': 'mail.theartfarm.com', - 'matt.simerson@gmail.com': /google.com/, - 'example.com': '', - 'no-mx.haraka.tnpi.net': '192.0.99.5', - 'bad-mx.haraka.tnpi.net': /99/, - 'über.haraka.tnpi.net': 'no-mx.haraka.tnpi.net', - } - - function checkValid(c, mxlist) { - try { - if ('string' === typeof c) { - assert.equal(mxlist[0].exchange, c) - } else { - assert.ok(c.test(mxlist[0].exchange)) - } - } catch (err) { - console.error(err) - } - } - - for (const c in validCases) { - it(`gets MX records for ${c}`, function (done) { - this.timeout(12000) - this.net_utils.get_mx(c, (err, mxlist) => { - if (err) console.error(err) - assert.ifError(err) - // assert.ok(mxlist.length); - checkValid(validCases[c], mxlist) - done() - }) - }) - - it(`awaits MX records for ${c}`, async function () { - this.timeout(12000) - const mxlist = await this.net_utils.get_mx(c) - // assert.ok(mxlist.length); - checkValid(validCases[c], mxlist) + it('adds a line processor', function (done) { + const socket = new EventEmitter() + let lines = 0 + socket.on('line', () => { + lines++ }) - } - - // macOS: ENODATA, win: ENOTOUND, ubuntu: ESERVFAIL - const invalidCases = { - invalid: /queryMx (ENODATA|ENOTFOUND|ESERVFAIL) invalid/, - 'gmail.xn--com-0da': /(ENOTFOUND|Cannot convert name to ASCII)/, - } - - function checkInvalid(expected, actual) { - if ('string' === typeof expected) { - assert.strictEqual(actual, expected) - } else { - assert.equal(expected.test(actual), true) - } - } - - for (const c in invalidCases) { - it(`cb does not crash on invalid name: ${c}`, function () { - this.net_utils.get_mx(c, (err, mxlist) => { - assert.equal(mxlist.length, 0) - checkInvalid(invalidCases[c], err.message) - }) - }) - - it(`async does not crash on invalid name: ${c}`, async function () { - try { - const mxlist = await this.net_utils.get_mx(c) - assert.equal(mxlist.length, 0) - } catch (err) { - checkInvalid(invalidCases[c], err.message) - } + socket.on('end', () => { + assert.equal(lines, 3) + done() }) - } -}) - -describe('resolve_mx_hosts', function () { - this.timeout(12000) - - beforeEach((done) => { - this.net_utils = require('../index') - done() - }) - - const expectedResolvedMx = [ - { - exchange: '2605:ae00:329::14', - priority: 10, - from_dns: 'mail.theartfarm.com', - }, - { - exchange: '66.128.51.165', - priority: 10, - from_dns: 'mail.theartfarm.com', - }, - ] - - it('resolves mx hosts to IPs, tnpi.net', async () => { - const r = await this.net_utils.resolve_mx_hosts([ - { exchange: 'mail.theartfarm.com', priority: 10, from_dns: 'tnpi.net' }, - ]) - assert.deepEqual(r, expectedResolvedMx) - }) - - it('resolves mx hosts to IPs, gmail.com', async () => { - const mxes = await this.net_utils.get_mx('gmail.com') - assert.equal(mxes.length, 5) - const r = await this.net_utils.resolve_mx_hosts(mxes) - assert.equal(r.length, 10) - }) - - it('returns IPs as is', async () => { - const r = await this.net_utils.resolve_mx_hosts(expectedResolvedMx) - assert.deepEqual(r, expectedResolvedMx) - }) - - it('returns sockets as-is', async () => { - const r = await this.net_utils.resolve_mx_hosts([{ path: '/var/run/sock' }]) - assert.deepEqual(r, [{ path: '/var/run/sock' }]) - }) - - it('resolve_mx_hosts, gmail.com', async () => { - const mxes = await this.net_utils.get_mx('gmail.com') - const r = await this.net_utils.resolve_mx_hosts(mxes) - assert.equal(r.length, 10) - }) - - it('resolve_mx_hosts, yahoo.com', async () => { - const mxes = await this.net_utils.get_mx('yahoo.com') - const r = await this.net_utils.resolve_mx_hosts([mxes[0]]) - assert.equal(r.length, 8) + this.net_utils.add_line_processor(socket) + socket.emit('data', `multi\nline\nallThisDataIsLost\n`) + socket.emit('end') }) })