|
import { |
|
fastPathLookup, |
|
IPublicSuffix, |
|
ISuffixLookupOptions, |
|
} from 'tldts-core'; |
|
import { exceptions, ITrie, rules } from './data/trie'; |
|
|
|
|
|
const enum RULE_TYPE { |
|
ICANN = 1, |
|
PRIVATE = 2, |
|
} |
|
|
|
interface IMatch { |
|
index: number; |
|
isIcann: boolean; |
|
isPrivate: boolean; |
|
} |
|
|
|
|
|
|
|
|
|
function lookupInTrie( |
|
parts: string[], |
|
trie: ITrie, |
|
index: number, |
|
allowedMask: number, |
|
): IMatch | null { |
|
let result: IMatch | null = null; |
|
let node: ITrie | undefined = trie; |
|
while (node !== undefined) { |
|
|
|
if ((node[0] & allowedMask) !== 0) { |
|
result = { |
|
index: index + 1, |
|
isIcann: node[0] === RULE_TYPE.ICANN, |
|
isPrivate: node[0] === RULE_TYPE.PRIVATE, |
|
}; |
|
} |
|
|
|
|
|
if (index === -1) { |
|
break; |
|
} |
|
|
|
const succ: { [label: string]: ITrie } = node[1]; |
|
node = Object.prototype.hasOwnProperty.call(succ, parts[index]!) |
|
? succ[parts[index]!] |
|
: succ['*']; |
|
index -= 1; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
export default function suffixLookup( |
|
hostname: string, |
|
options: ISuffixLookupOptions, |
|
out: IPublicSuffix, |
|
): void { |
|
if (fastPathLookup(hostname, options, out)) { |
|
return; |
|
} |
|
|
|
const hostnameParts = hostname.split('.'); |
|
|
|
const allowedMask = |
|
(options.allowPrivateDomains ? RULE_TYPE.PRIVATE : 0) | |
|
(options.allowIcannDomains ? RULE_TYPE.ICANN : 0); |
|
|
|
|
|
const exceptionMatch = lookupInTrie( |
|
hostnameParts, |
|
exceptions, |
|
hostnameParts.length - 1, |
|
allowedMask, |
|
); |
|
|
|
if (exceptionMatch !== null) { |
|
out.isIcann = exceptionMatch.isIcann; |
|
out.isPrivate = exceptionMatch.isPrivate; |
|
out.publicSuffix = hostnameParts.slice(exceptionMatch.index + 1).join('.'); |
|
return; |
|
} |
|
|
|
|
|
const rulesMatch = lookupInTrie( |
|
hostnameParts, |
|
rules, |
|
hostnameParts.length - 1, |
|
allowedMask, |
|
); |
|
|
|
if (rulesMatch !== null) { |
|
out.isIcann = rulesMatch.isIcann; |
|
out.isPrivate = rulesMatch.isPrivate; |
|
out.publicSuffix = hostnameParts.slice(rulesMatch.index).join('.'); |
|
return; |
|
} |
|
|
|
|
|
|
|
|
|
out.isIcann = false; |
|
out.isPrivate = false; |
|
out.publicSuffix = hostnameParts[hostnameParts.length - 1] ?? null; |
|
} |
|
|