IW4M-Admin/Plugins/ScriptPlugins/SubnetBan.js
2023-04-04 18:24:13 -05:00

205 lines
7.0 KiB
JavaScript

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 = '<table class="table bg-dark-dm bg-light-lm">';
const unbanSubnetInteraction = {
InteractionId: 'command',
Data: 'unbansubnet',
ActionButtonLabel: 'Unban',
Name: 'Unban Subnet'
};
subnetList.forEach(subnet => {
unbanSubnetInteraction.Data += ' ' + subnet;
table += `<tr>
<td>
<p>${subnet}</p>
</td>
<td>
<a href="#" class="profile-action no-decoration float-right" data-action="DynamicAction"
data-action-meta="${encodeURI(JSON.stringify(unbanSubnetInteraction))}">
<div class="btn">
<i class="oi oi-circle-x mr-5 font-size-12"></i>
<span class="text-truncate">Unban Subnet</span>
</div>
</a>
</td>
</tr>`;
});
table += '</table>';
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;
};