Skip to content

Commit 08ed923

Browse files
committed
GanneffServ: make it possible to not kill irccloud
Without this, anyone using irccloud (or a similar service) can get all other users k:lined by tripping on a trap (J). With this, they should be treated like a tor user (getting themselves killed).
1 parent 9802676 commit 08ed923

File tree

3 files changed

+134
-2
lines changed

3 files changed

+134
-2
lines changed

languages/ganneffserv.en.lang

+15
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,18 @@ GS_HLP_SRV_LONG
7575
IN ROTATION.
7676

7777
EVERY user that connects on this server WILL BE KLINED!
78+
GS_HLP_PRT_SHORT
79+
%s: Protect users from akill triggered by another similar user
80+
GS_HLP_PRT_LONG
81+
Takes a hostmask (or a regular expression if it starts with ^).
82+
83+
When a matching user trips on a trap, only they will be killed
84+
instead of causing collateral damage to other matching users.
85+
86+
Usage: PROTECT <mask|regex> :<reason>
87+
GS_HLP_UPR_SHORT
88+
%s: Delete protection for adjacent users
89+
GS_HLP_UPR_LONG
90+
Delete collateral damage protection for matching users.
91+
92+
Usage: UNPROTECT <mask|regex>

modules/GanneffServ.rb

+109-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ def initialize()
6969
["STATS", 0, 0, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_STS_SHORT'), lm('GS_HLP_STS_LONG')],
7070
["ENFORCE", 0, 0, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_ENF_SHORT'), lm('GS_HLP_ENF_LONG')],
7171
["BADSERV", 0, 1, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_SRV_SHORT'), lm('GS_HLP_SRV_LONG')],
72+
["PROTECT", 1, 2, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_PRT_SHORT'), lm('GS_HLP_PRT_LONG')],
73+
["UNPROTECT",1, 2, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_UPR_SHORT'), lm('GS_HLP_UPR_LONG')],
7274
]) # register
7375

7476
# Which hooks do we want?
@@ -100,6 +102,13 @@ def initialize()
100102
irc_lower($1)')
101103
@dbq['INCREASE_KILLS'] = DB.prepare('UPDATE ganneffserv SET kills = kills+1
102104
WHERE irc_lower(channel) = irc_lower($1)')
105+
@dbq['INSERT_PROTECT'] = DB.prepare('INSERT INTO ganneffprotect(setter, time,
106+
pattern, reason) VALUES($1, $2, $3, $4)')
107+
@dbq['DELETE_PROTECT'] = DB.prepare('DELETE FROM ganneffprotect WHERE
108+
irc_lower(pattern) = irc_lower($1)')
109+
@dbq['GET_PROTECTED_PATTERNS'] = DB.prepare('SELECT pattern, reason FROM ganneffprotect')
110+
@dbq['GET_PROTECTED_PATTERNS_DETAILED'] = DB.prepare('SELECT pattern, setter, time,
111+
reason FROM ganneffprotect')
103112
end # def initialize
104113

105114
########################################################################
@@ -221,6 +230,48 @@ def DEL(client, parv = [])
221230
true
222231
end # def DEL
223232

233+
# ------------------------------------------------------------------------
234+
235+
# Protect users from collatoral damage
236+
def PROTECT(client, parv = [])
237+
parv[1].downcase!
238+
debug(LOG_DEBUG, "#{client.name} called PROTECT and the params are #{parv.join(",")}")
239+
240+
requested_pattern = parv[1]
241+
pattern = irc_pattern_to_regex(requested_pattern)
242+
reason = parv[2]
243+
244+
ret = DB.execute_nonquery(@dbq['INSERT_PROTECT'], 'iiss', client.nick.account_id,
245+
Time.now.to_i, pattern, reason)
246+
if ret then
247+
debug(LOG_NOTICE, "#{client.name} added protection #{pattern}, reason #{reason}")
248+
@protection[pattern] = reason
249+
load_protected_patterns
250+
reply(client, "Protection #{requested_pattern} successfully added")
251+
else
252+
reply(client, "Failed to add #{requested_pattern}")
253+
end
254+
255+
# ------------------------------------------------------------------------
256+
257+
# Unprotect users from collatoral damage
258+
def UNPROTECT(client, parv = [])
259+
parv[1].downcase!
260+
debug(LOG_DEBUG, "#{client.name} called UNPROTECT and the params are #{parv.join(",")}")
261+
262+
pattern = irc_pattern_to_regex(parv[1])
263+
return unless @protection.has_key?(pattern)
264+
265+
ret = DB.execute_nonquery(@dbq['DELETE_PROTECT'], 's', pattern)
266+
if ret then
267+
debug(LOG_NOTICE, "#{client.name} removed protection #{pattern}")
268+
@protection.delete(pattern)
269+
load_protected_patterns
270+
reply(client, "Protection #{pattern} successfully deleted.")
271+
else
272+
reply(client, "Failed to delete protection #{pattern}.")
273+
end
274+
224275
# ------------------------------------------------------------------------
225276

226277
# List all channels we monitor
@@ -245,6 +296,18 @@ def LIST(client, parv = [])
245296
}
246297
result.free
247298

299+
reply(client, "Protected host patterns\n\n")
300+
reply(client, "%-50s %-10s %-19s %s" % [ "Pattern", "By", "When", "Reason" ])
301+
result = DB.execute(@dbq['GET_PROTECTED_PATTERNS_DETAILED'])
302+
result.row_each { |row|
303+
pattern = row[0]
304+
by = row[1]
305+
time = Time.at(row[2].to_i).strftime('%Y-%m-%d %H:%M:%S')
306+
reason = row[3]
307+
reply(client, "%-50s %-10s %-19s %s" % [ pattern, by, time, reason ])
308+
}
309+
result.free
310+
248311
reply(client, "\nCRFJ - checks Connect, Register nick, Join channel within 15 seconds (i.e. Fast)")
249312
reply(client, "J - triggers on every Join")
250313

@@ -574,8 +637,13 @@ def akill(client, reason, operreason, channel="")
574637
ret = kill_user(client, reason)
575638
else # if host
576639
reason = "#{reason}|#{operreason}"
577-
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
578-
ret = akill_add("*@#{host}", reason, @akill_duration)
640+
if client.host =~ /#{@protected_patterns}/i # if protected hosts
641+
debug(LOG_DEBUG, "Using /kill instead of AKILL for protected user #{client.name}")
642+
ret = kill_user(client, reason)
643+
else
644+
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
645+
ret = akill_add("*@#{host}", reason, @akill_duration)
646+
end # if protected hosts
579647
end # if host
580648

581649
channel.downcase!
@@ -596,6 +664,31 @@ def akill(client, reason, operreason, channel="")
596664
end # if kill_user
597665
end # def akill
598666

667+
# ------------------------------------------------------------------------
668+
669+
# convert irc pattern to regular expression
670+
def irc_pattern_to_regex(pattern
671+
# "." -> "\.", "*" -> ".*", "?" -> "."
672+
# wrap with "^...$"
673+
return pattern if pattern.start_with? '^'
674+
pattern = pattern.gsub(/\./, '\\.')
675+
.gsub(/\*/, '.*')
676+
.gsub(/\?/, '.')
677+
return "^#{pattern}$"
678+
end
679+
680+
# ------------------------------------------------------------------------
681+
682+
# get protected patterns as pattern
683+
def load_protected_patterns()
684+
patterns = @protection.keys
685+
if patterns.empty?
686+
@protected_patterns = '^$'
687+
else
688+
@protected_patterns = patterns.join('|')
689+
end
690+
end # def get_protected_patterns
691+
599692
# ------------------------------------------------------------------------
600693

601694
# enforce a channel - kill all of its users
@@ -648,6 +741,20 @@ def load_data()
648741
count += 1
649742
}
650743
result.free
744+
745+
@protection = Hash.new
746+
result = DB.execute(@dbq['GET_PROTECTED_PATTERNS'], '')
747+
count = 0
748+
result.row_each { |row|
749+
pattern = row[0]
750+
reason = row[1]
751+
pattern = irc_pattern_to_regex(pattern)
752+
@protection[pattern] = reason
753+
count += 1
754+
}
755+
result.free
756+
load_protected_patterns
757+
651758
debug(LOG_DEBUG, "All channel data successfully loaded")
652759
end # def load_data
653760

sql/ganneffserv-pgsql.sql

+10
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,14 @@ CREATE TABLE ganneffserv (
88
kills INTEGER NOT NULL DEFAULT 0,
99
monitor_only BOOLEAN NOT NULL DEFAULT 'False'
1010
);
11+
12+
DROP TABLE IF EXISTS ganneffprotect CASCADE;
13+
CREATE TABLE ganneffprotect (
14+
id SERIAL PRIMARY KEY,
15+
setter INTEGER REFERENCES account(id) ON DELETE SET NULL,
16+
time INTEGER NOT NULL,
17+
pattern VARCHAR(255) NOT NULL,
18+
reason VARCHAR(255) NOT NULL
19+
)
20+
1121
CREATE UNIQUE INDEX ganneffserv_channel_idx ON ganneffserv (irc_lower(channel));

0 commit comments

Comments
 (0)