Skip to content

Commit aca1729

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 aca1729

File tree

3 files changed

+138
-2
lines changed

3 files changed

+138
-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

+113-2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ 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')],
74+
]
7275
]) # register
7376

7477
# Which hooks do we want?
@@ -100,6 +103,15 @@ def initialize()
100103
irc_lower($1)')
101104
@dbq['INCREASE_KILLS'] = DB.prepare('UPDATE ganneffserv SET kills = kills+1
102105
WHERE irc_lower(channel) = irc_lower($1)')
106+
@dbq['INSERT_PROTECT'] = DB.prepare('INSERT INTO ganneffprotect(setter, time,
107+
pattern, reason) VALUES($1, $2, $3, $4)')
108+
@dbq['DELETE_PROTECT'] = DB.prepare('DELETE FROM ganneffprotect WHERE
109+
irc_lower(pattern) = irc_lower($1)')
110+
@dbq['GET_PROTECTED_PATTERNS'] = DB.prepare('SELECT pattern, reason FROM ganneffprotect')
111+
@dbq['GET_PROTECTED_PATTERNS_DETAILED'] = DB.prepare('SELECT pattern, setter, time,
112+
reason FROM ganneffprotect')
113+
114+
load_protected_patterns
103115
end # def initialize
104116

105117
########################################################################
@@ -221,6 +233,48 @@ def DEL(client, parv = [])
221233
true
222234
end # def DEL
223235

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

226280
# List all channels we monitor
@@ -245,6 +299,18 @@ def LIST(client, parv = [])
245299
}
246300
result.free
247301

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

@@ -574,8 +640,13 @@ def akill(client, reason, operreason, channel="")
574640
ret = kill_user(client, reason)
575641
else # if host
576642
reason = "#{reason}|#{operreason}"
577-
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
578-
ret = akill_add("*@#{host}", reason, @akill_duration)
643+
if client.host =~ /#{@protected_patterns}/i # if protected hosts
644+
debug(LOG_DEBUG, "Using /kill instead of AKILL for protected user #{client.name}")
645+
ret = kill_user(client, reason)
646+
else
647+
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
648+
ret = akill_add("*@#{host}", reason, @akill_duration)
649+
end # if protected hosts
579650
end # if host
580651

581652
channel.downcase!
@@ -596,6 +667,31 @@ def akill(client, reason, operreason, channel="")
596667
end # if kill_user
597668
end # def akill
598669

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

601697
# enforce a channel - kill all of its users
@@ -648,6 +744,21 @@ def load_data()
648744
count += 1
649745
}
650746
result.free
747+
748+
@protection = Hash.new
749+
result = DB.execute(@dbq['GET_PROTECTED_PATTERNS'], '')
750+
count = 0
751+
result.row_each { |row|
752+
pattern = row[0]
753+
reason = row[1]
754+
pattern = irc_pattern_to_regex(pattern)
755+
pattern = Regexp.escape(pattern)
756+
@protection[pattern] = reason
757+
count += 1
758+
}
759+
result.free
760+
load_protected_patterns
761+
651762
debug(LOG_DEBUG, "All channel data successfully loaded")
652763
end # def load_data
653764

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)