IPCache.ts
1 import { AddressInfo, BlockList, IPVersion } from 'node:net' 2 import { L2LoginDataApi } from './L2LoginDataApi' 3 import { ServerLog } from '../logger/Logger' 4 import { ConfigManager } from '../config/ConfigManager' 5 import { Socket } from 'net' 6 7 export enum IPCacheConnectionType { 8 GameClient, 9 GameServer, 10 CommandLink 11 } 12 13 export enum IPCacheListType { 14 Block, 15 Allow 16 } 17 18 class Manager implements L2LoginDataApi { 19 private blockList : Record<number, BlockList> 20 private allowList : Record<number, BlockList> 21 22 private loadConfiguration() : void { 23 this.blockList = { 24 [ IPCacheConnectionType.GameClient ]: new BlockList(), 25 } 26 27 this.allowList = { 28 [ IPCacheConnectionType.GameServer ]: new BlockList(), 29 [ IPCacheConnectionType.CommandLink ]: new BlockList(), 30 } 31 32 if ( ConfigManager.server.getClientBlockListIps().length > 0 ) { 33 this.loadRules( this.blockList[ IPCacheConnectionType.GameClient ], ConfigManager.server.getClientBlockListIps(), IPCacheConnectionType[ IPCacheConnectionType.GameClient ] ) 34 } 35 36 if ( ConfigManager.server.getGameServerAllowListIps().length > 0 ) { 37 this.loadRules( this.allowList[ IPCacheConnectionType.GameServer ], ConfigManager.server.getGameServerAllowListIps(), IPCacheConnectionType[ IPCacheConnectionType.GameServer ] ) 38 } 39 40 if ( ConfigManager.server.getCommandLinkAllowedIps().length > 0 ) { 41 this.loadRules( this.allowList[ IPCacheConnectionType.CommandLink ], ConfigManager.server.getCommandLinkAllowedIps(), IPCacheConnectionType[ IPCacheConnectionType.CommandLink ] ) 42 } 43 } 44 45 private loadRules( ruleList: BlockList, rules: Array<string>, name: string ) : void { 46 for ( const rule of rules ) { 47 const chunks = rule.split( '/' ) 48 49 try { 50 switch ( chunks.length ) { 51 case 1: 52 ruleList.addAddress( chunks[ 0 ] ) 53 break 54 55 case 2: 56 ruleList.addSubnet( chunks[ 0 ], parseInt( chunks[ 1 ], 10 ) ) 57 break 58 } 59 } catch ( error ) { 60 ServerLog.error( error, 'IPCache: %s failed to load %s', name, rule ) 61 } 62 } 63 } 64 65 isInList( socket: Socket, type: IPCacheConnectionType, listType: IPCacheListType ) : boolean { 66 switch ( listType ) { 67 case IPCacheListType.Allow: 68 return this.isInAllowList( socket, type ) 69 70 case IPCacheListType.Block: 71 return this.isInBlockList( socket, type ) 72 } 73 } 74 75 private isInAllowList( socket: Socket, type: IPCacheConnectionType ) : boolean { 76 const list = this.allowList[ type ] 77 if ( !list || list.rules.length === 0 ) { 78 return true 79 } 80 81 return this.checkListSocket( list, socket ) 82 } 83 84 private isInBlockList( socket: Socket, type: IPCacheConnectionType ) : boolean { 85 const list = this.blockList[ type ] 86 87 if ( !list || list.rules.length === 0 ) { 88 return false 89 } 90 91 return this.checkListSocket( list, socket ) 92 } 93 94 private checkListSocket( list: BlockList, socket: Socket ) : boolean { 95 const address = socket.address() as AddressInfo 96 return list.check( address.address ?? socket.remoteAddress, ( address.family ?? socket.remoteFamily ) as IPVersion ) 97 } 98 99 isAddressInBlockList( address: string, version: IPVersion, type: IPCacheConnectionType ) : boolean { 100 const list = this.blockList[ type ] 101 if ( !list || list.rules.length === 0 ) { 102 return false 103 } 104 105 return list.check( address, version ) 106 } 107 108 async load(): Promise<void> { 109 this.loadConfiguration() 110 ConfigManager.server.subscribe( this.loadConfiguration.bind( this ) ) 111 112 this.describe( this.blockList, IPCacheListType[ IPCacheListType.Block ] ) 113 this.describe( this.allowList, IPCacheListType[ IPCacheListType.Allow ] ) 114 } 115 116 private describe( list : Record<number, BlockList>, name: string ) : void { 117 Object.keys( list ).map( ( key : string ) : void => { 118 ServerLog.info( 'IPCache %s: %s loaded %d rules.', name, IPCacheConnectionType[ key ], list[ key ].rules.length ) 119 } ) 120 } 121 } 122 123 export const IPCache = new Manager()