const cidrRegex = /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/; const validCIDR = input => cidrRegex.test(input); const subnetBanlistKey = 'Webfront::Nav::Admin::SubnetBanlist'; let subnetList = []; const init = (registerNotify, serviceResolver, config) => { registerNotify('IManagementEventSubscriptions.ClientStateAuthorized', (authorizedEvent, _) => plugin.onClientAuthorized(authorizedEvent)); plugin.onLoad(serviceResolver, config); return plugin; }; const plugin = { author: 'RaidMax', version: '2.0', name: 'Subnet Banlist Plugin', manager: null, logger: null, config: null, serviceResolver: null, banMessage: '', commands: [{ name: 'bansubnet', description: 'bans an IPv4 subnet', alias: 'bs', permission: 'SeniorAdmin', targetRequired: false, arguments: [{ name: 'subnet in IPv4 CIDR notation', required: true }], execute: (gameEvent) => { const input = String(gameEvent.data).trim(); if (!validCIDR(input)) { gameEvent.origin.tell('Invalid CIDR input'); return; } subnetList.push(input); plugin.config.setValue('SubnetBanList', subnetList); gameEvent.origin.tell(`Added ${input} to subnet banlist`); } }, { name: 'unbansubnet', description: 'unbans an IPv4 subnet', alias: 'ubs', permission: 'SeniorAdmin', targetRequired: false, arguments: [{ name: 'subnet in IPv4 CIDR notation', required: true }], execute: (gameEvent) => { const input = String(gameEvent.data).trim(); if (!validCIDR(input)) { gameEvent.origin.tell('Invalid CIDR input'); return; } if (!subnetList.includes(input)) { gameEvent.origin.tell('Subnet is not banned'); return; } subnetList = subnetList.filter(item => item !== input); plugin.config.setValue('SubnetBanList', subnetList); gameEvent.origin.tell(`Removed ${input} from subnet banlist`); } } ], interactions: [{ name: subnetBanlistKey, action: function (_, __, ___) { const helpers = importNamespace('SharedLibraryCore.Helpers'); const interactionData = new helpers.InteractionData(); interactionData.name = 'Subnet Banlist'; // navigation link name interactionData.description = `List of banned subnets (${subnetList.length} Total)`; // alt and title interactionData.displayMeta = 'oi-circle-x'; // nav icon interactionData.interactionId = subnetBanlistKey; interactionData.minimumPermission = 3; interactionData.interactionType = 2; interactionData.source = plugin.name; interactionData.ScriptAction = (sourceId, targetId, game, meta, token) => { let table = ''; const unbanSubnetInteraction = { InteractionId: 'command', Data: 'unbansubnet', ActionButtonLabel: 'Unban', Name: 'Unban Subnet' }; subnetList.forEach(subnet => { unbanSubnetInteraction.Data += ' ' + subnet; table += ``; }); table += '

${subnet}

Unban Subnet
'; return table; }; return interactionData; } }], onLoad: function (serviceResolver, config) { this.serviceResolver = serviceResolver; this.config = config; this.logger = serviceResolver.resolveService('ILogger', ['ScriptPluginV2']); subnetList = []; const list = this.config.getValue('SubnetBanList'); if (list !== undefined) { list.forEach(element => { const ban = String(element); subnetList.push(ban); }); this.logger.logInformation('Loaded {Count} banned subnets', list.length); } else { this.config.setValue('SubnetBanList', []); } this.banMessage = this.config.getValue('BanMessage'); if (this.banMessage === undefined) { this.banMessage = 'You are not allowed to join this server.'; this.config.setValue('BanMessage', this.banMessage); } const interactionRegistration = serviceResolver.resolveService('IInteractionRegistration'); interactionRegistration.unregisterInteraction(subnetBanlistKey); this.logger.logInformation('Subnet Ban loaded'); }, onClientAuthorized: (clientEvent) => { if (!isSubnetBanned(clientEvent.client.ipAddressString, subnetList)) { return; } this.logger.logInformation(`Kicking {Client} because they are subnet banned.`, clientEvent.client); clientEvent.client.kick(this.banMessage, clientEvent.client.currentServer.asConsoleClient()); } }; const convertIPtoLong = ip => { let components = String(ip).match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/); if (components) { let ipLong = 0; let power = 1; for (let i = 4; i >= 1; i -= 1) { ipLong += power * parseInt(components[i]); power *= 256; } return ipLong; } else { return -1; } }; const isInSubnet = (ip, subnet) => { const mask = subnet.match(/^(.*?)\/(\d{1,2})$/); if (!mask) { return false; } const baseIP = convertIPtoLong(mask[1]); const longIP = convertIPtoLong(ip); if (mask && baseIP >= 0) { const freedom = Math.pow(2, 32 - parseInt(mask[2])); return (longIP > baseIP) && (longIP < baseIP + freedom - 1); } else return false; }; const isSubnetBanned = (ip, list) => { const matchingSubnets = list.filter(subnet => isInSubnet(ip, subnet)); return matchingSubnets.length !== 0; };