main
ts 52 lines 2.2 KB
Raw
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 import { defineConfig } from './config'
4 import { disconnect, getConnections, normalizeIp } from './connections'
5 import { makeNetMatcher, MINUTE, netMatches, onlyTruthy } from './misc'
6 import { isIP, Socket } from 'net'
7 import _ from 'lodash'
8
9 export interface BlockingRule { ip: string, comment?: string, expire?: Date, disabled?: boolean }
10
11 export const block = defineConfig('block', [] as BlockingRule[], rules => {
12 const now = new Date()
13 const ret = !Array.isArray(rules) ? []
14 : onlyTruthy(rules.map(rule => {
15 rule.expire &&= new Date(rule.expire)
16 return !rule.disabled && (rule.expire || now) >= now && makeNetMatcher(rule.ip)
17 }))
18 setTimeout(() => { // wait until defineConfig has stored the newly compiled rules, because applyBlock uses isBlocked, that uses block.compiled
19 for (const { socket, ip } of getConnections()) // reapply new block to existing connections
20 applyBlock(socket, ip)
21 })
22 return ret
23 })
24
25 export function applyBlock(socket: Socket, ip=normalizeIp(socket.remoteAddress||'')) {
26 if (ip && isBlocked(ip))
27 return disconnect(socket, 'block-ip')
28 }
29
30 export function isBlocked(ip: string) {
31 return block.compiled().find(rule => rule(ip))
32 }
33
34 setInterval(() => { // twice a minute, check if any block has expired
35 const now = new Date()
36 const next = block.get().filter(x => !x.expire || x.expire > now)
37 const n = block.get().length - next.length
38 if (!n) return
39 console.log("Blocking rules:", n, "expired")
40 block.set(next)
41 }, MINUTE/2)
42
43 export function addBlock(rule: BlockingRule, merge?: Partial<BlockingRule>) {
44 if (isIP(rule.ip) && isBlocked(rule.ip)) return // already
45 block.set(was => {
46 const match = merge && _.matches(merge)
47 const foundIdx = match ? _.findIndex(was, v => match(v) && !v.disabled) : -1
48 // in case the rule is disabled, and isBlocked returned false
49 return foundIdx < 0 ? [...was, { ...merge, ...rule }] // add as new rule
50 : was.map((x, i) => i === foundIdx ? { ...x, ...rule, ip: `${x.ip}|${rule.ip}` } : x)
51 })
52 }