/ login-server / source / cache / IPCache.ts
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()