import { createDebug , ipv6ToLong } from "./utils" ; import { existsSync , readFileSync } from "fs" ; import { resolve as pathResolve , isAbsolute } from "path" ; import { isIPv6 } from "net" ; import Ipv4ToRegion , { Ipv4ToRegionRes , Ipv4ToRegionResult } from "./ipv4" ; const debug = createDebug ( "ipv6" ) ; /**
 * IP 结果
 */ export interface Ipv6ToRegionRes { /** 区域字符串 */ cArea : string ; /** 运营商 */ aArea : string ; } /**
 * IP 解析结果
 */ export interface Ipv6ToRegionResult { /** 国家 */ country : string ; /** 省份 */ province : string ; /** 城市 */ city : string ; /** ISP 供应商 */ isp : string ; /** 原始数据 */ data : string ; } export default class Ipv6ToRegion { /**  数据库文件位置 */ private dbFilePath : string ; private data : Buffer ; private name : string ; private offlen : number ; private record : number ; private indexStart : number ; private ipv4 ? : Ipv4ToRegion ; constructor ( dbPath ? ) { const p = dbPath || "../../data/ipv6wry.db" ; this . dbFilePath = isAbsolute ( p ) ? p : pathResolve ( __dirname , p ) ; if ( ! existsSync ( this . dbFilePath ) ) { throw new Error ( "[Ipv6ToRegion] db file not exists : " + this . dbFilePath ) ; } this . data = readFileSync ( this . dbFilePath ) ; this . name = this . data . toString ( "utf-8" , 0 , 4 ) ; if ( this . name !== "IPDB" ) { throw new Error ( "[Ipv6ToRegion] db file error" ) ; } this . offlen = this . data . readInt8 ( 6 ) ; this . record = Number ( this . data . readBigInt64LE ( 8 ) ) ; this . indexStart = Number ( this . data . readBigInt64LE ( 16 ) ) ; } setIpv4Ins ( ins ) { this . ipv4 = ins ; } private searchIpv4 ( ip ) { if ( ! this . ipv4 ) { return { cArea : "未知" , aArea : "未知" } ; } debug ( "searchIpv4" , Number ( ip ) ) ; return this . ipv4 . searchLong ( Number ( ip ) ) ; } param offset private readLongData ( offset ) { return this . data . readIntLE ( offset , this . offlen ) ; } param ip param l param r private find ( ip , l , r ) { if ( r - l <= 1 ) { return l ; } const m = ( l + r ) >> 1 ; const o = this . indexStart + m * ( 8 + this . offlen ) ; const new_ip = this . data . readBigUInt64LE ( o ) ; if ( ip < new_ip ) { return this . find ( ip , l , m ) ; } else { return this . find ( ip , m , r ) ; } } param offset private getString ( offset = 0 ) { const o2 = this . data . indexOf ( "\0" , offset ) ; return this . data . toString ( "utf-8" , offset , o2 ) ; } param offset private getAreaAddr ( offset = 0 ) { const byte = this . data . readInt8 ( offset ) ; if ( byte == 1 || byte == 2 ) { const p = this . readLongData ( offset + 1 ) ; return this . getAreaAddr ( p ) ; } else { return this . getString ( offset ) ; } } param offset private getAddr ( offset ) { let o = offset ; const byte = this . data . readInt8 ( o ) ; debug ( byte ) ; if ( byte === 1 ) { return this . getAddr ( this . readLongData ( o + 1 ) ) ; } else { const cArea = this . getAreaAddr ( o ) ; debug ( cArea ) ; if ( byte == 2 ) { o += 1 + this . offlen ; } else { o = this . data . indexOf ( "\0" , o ) + 1 ; } const aArea = this . getAreaAddr ( o ) ; debug ( aArea ) ; return { cArea , aArea } ; } } private parseResult ( ret ) { if ( ret === null ) return ret ; if ( "city" in ret ) { return this . ipv4 ? this . ipv4 . parseResult ( ret ) : null ; } let country = "" ; let province = "" ; let city = "" ; let first = ret . cArea . indexOf ( "国" ) ; if ( first === 1 ) { first += 1 ; country = ret . cArea . slice ( 0 , first ) ; } else { first = 0 ; } let isCity = false ; let second = ret . cArea . indexOf ( "省" ) ; if ( second >= 0 ) { second += 1 ; province = ret . cArea . slice ( first , second ) ; } else { const provinceArr = [ "内蒙古" , "广西" , "西藏" , "宁夏" , "新疆" ] ; const goverCity = [ "北京市" , "天津市" , "上海市" , "重庆市" ] ; for ( let gover of goverCity ) { second = ret . cArea . indexOf ( gover ) ; if ( second >= 0 ) { second = second + gover . length ; province = ret . cArea . slice ( first , second ) ; isCity = true ; break ; } } if ( isCity != true ) { for ( let p of provinceArr ) { second = ret . cArea . indexOf ( p ) ; if ( second >= 0 ) { second = second + p . length ; province = ret . cArea . slice ( first , second ) ; break ; } } } } if ( isCity != true ) { const city1 = ret . cArea . indexOf ( "市" ) ; const city2 = ret . cArea . indexOf ( "州" ) ; if ( city1 >= 0 && city2 >= 0 && city1 < city2 && second < city1 ) { city = ret . cArea . slice ( second , city1 + 1 ) ; } else if ( city1 >= 0 && city2 >= 0 && city1 > city2 && second < city2 ) { if ( city1 - city2 == 1 ) { city = ret . cArea . slice ( second , city2 + 2 ) ; } else { city = ret . cArea . slice ( second , city2 + 1 ) ; } } else if ( city1 >= 0 && second < city1 ) { city = ret . cArea . slice ( second , city1 + 1 ) ; } else if ( city2 >= 0 && second < city2 ) { city = ret . cArea . slice ( second , city2 + 1 ) ; } } return { isp : ret . aArea , data : ret . cArea , country , province , city } ; } private getIpAddrLong ( ip ) { const idx = this . find ( ip , 0 , this . record ) ; debug ( idx ) ; const ipOff = this . indexStart + idx * ( 8 + this . offlen ) ; const ipRecOff = this . readLongData ( ipOff + 8 ) ; debug ( { ipOff , ipRecOff } ) ; return this . getAddr ( ipRecOff ) ; } searchLong ( ip6 ) { if ( ip6 === 0x1n ) { return { cArea : "IANA保留地址" , aArea : "本机地址" } ; } const ip = ( ip6 >> 64n ) & 0xffffffffffffffffn ; debug ( "ip" , ip ) ; if ( ip == 0n ) { debug ( "IPv4映射地址" ) ; const realip = ip6 & 0xffffffffn ; return this . searchIpv4 ( realip ) ; } if ( ( ( ip >> 48n ) & 0xffffn ) === 0x2002n ) { debug ( "6to4" ) ; const realip = ( ip & 0x0000ffffffff0000n ) >> 16n ; return this . searchIpv4 ( realip ) ; } if ( ( ( ip >> 32n ) & 0xffffffffn ) == 0x20010000n ) { debug ( "teredo" ) ; const realip = ~ ip6 & 0xffffffffn ; return this . searchIpv4 ( realip ) ; } if ( ( ( ip6 >> 32n ) & 0xffffn ) == 0x5efen ) { debug ( "isatap" ) ; const realip = ip6 & 0xffffffffn ; return this . searchIpv4 ( realip ) ; } debug ( "IPv6" ) ; return this . getIpAddrLong ( ip ) ; } search ( ipaddr , parse = true ) { const { ip , num } = ipv6ToLong ( ipaddr ) ; if ( ! isIPv6 ( ip ) ) return null ; debug ( { ipaddr , ip , num } ) ; const ret = this . searchLong ( num ) ; debug ( ret ) ; return parse ? this . parseResult ( ret ) : ret ; } }