Skip to content

Commit

Permalink
GanneffServ: make it possible to not kill irccloud
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
jsoref committed Nov 15, 2021
1 parent 9802676 commit 08ed923
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 2 deletions.
15 changes: 15 additions & 0 deletions languages/ganneffserv.en.lang
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,18 @@ GS_HLP_SRV_LONG
IN ROTATION.

EVERY user that connects on this server WILL BE KLINED!
GS_HLP_PRT_SHORT
%s: Protect users from akill triggered by another similar user
GS_HLP_PRT_LONG
Takes a hostmask (or a regular expression if it starts with ^).

When a matching user trips on a trap, only they will be killed
instead of causing collateral damage to other matching users.

Usage: PROTECT <mask|regex> :<reason>
GS_HLP_UPR_SHORT
%s: Delete protection for adjacent users
GS_HLP_UPR_LONG
Delete collateral damage protection for matching users.

Usage: UNPROTECT <mask|regex>
111 changes: 109 additions & 2 deletions modules/GanneffServ.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def initialize()
["STATS", 0, 0, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_STS_SHORT'), lm('GS_HLP_STS_LONG')],
["ENFORCE", 0, 0, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_ENF_SHORT'), lm('GS_HLP_ENF_LONG')],
["BADSERV", 0, 1, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_SRV_SHORT'), lm('GS_HLP_SRV_LONG')],
["PROTECT", 1, 2, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_PRT_SHORT'), lm('GS_HLP_PRT_LONG')],
["UNPROTECT",1, 2, SFLG_NOMAXPARAM, ADMIN_FLAG, lm('GS_HLP_UPR_SHORT'), lm('GS_HLP_UPR_LONG')],
]) # register

# Which hooks do we want?
Expand Down Expand Up @@ -100,6 +102,13 @@ def initialize()
irc_lower($1)')
@dbq['INCREASE_KILLS'] = DB.prepare('UPDATE ganneffserv SET kills = kills+1
WHERE irc_lower(channel) = irc_lower($1)')
@dbq['INSERT_PROTECT'] = DB.prepare('INSERT INTO ganneffprotect(setter, time,
pattern, reason) VALUES($1, $2, $3, $4)')
@dbq['DELETE_PROTECT'] = DB.prepare('DELETE FROM ganneffprotect WHERE
irc_lower(pattern) = irc_lower($1)')
@dbq['GET_PROTECTED_PATTERNS'] = DB.prepare('SELECT pattern, reason FROM ganneffprotect')
@dbq['GET_PROTECTED_PATTERNS_DETAILED'] = DB.prepare('SELECT pattern, setter, time,
reason FROM ganneffprotect')
end # def initialize

########################################################################
Expand Down Expand Up @@ -221,6 +230,48 @@ def DEL(client, parv = [])
true
end # def DEL

# ------------------------------------------------------------------------

# Protect users from collatoral damage
def PROTECT(client, parv = [])
parv[1].downcase!
debug(LOG_DEBUG, "#{client.name} called PROTECT and the params are #{parv.join(",")}")

requested_pattern = parv[1]
pattern = irc_pattern_to_regex(requested_pattern)
reason = parv[2]

ret = DB.execute_nonquery(@dbq['INSERT_PROTECT'], 'iiss', client.nick.account_id,
Time.now.to_i, pattern, reason)
if ret then
debug(LOG_NOTICE, "#{client.name} added protection #{pattern}, reason #{reason}")
@protection[pattern] = reason
load_protected_patterns
reply(client, "Protection #{requested_pattern} successfully added")
else
reply(client, "Failed to add #{requested_pattern}")
end

# ------------------------------------------------------------------------

# Unprotect users from collatoral damage
def UNPROTECT(client, parv = [])
parv[1].downcase!
debug(LOG_DEBUG, "#{client.name} called UNPROTECT and the params are #{parv.join(",")}")

pattern = irc_pattern_to_regex(parv[1])
return unless @protection.has_key?(pattern)

ret = DB.execute_nonquery(@dbq['DELETE_PROTECT'], 's', pattern)
if ret then
debug(LOG_NOTICE, "#{client.name} removed protection #{pattern}")
@protection.delete(pattern)
load_protected_patterns
reply(client, "Protection #{pattern} successfully deleted.")
else
reply(client, "Failed to delete protection #{pattern}.")
end

# ------------------------------------------------------------------------

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

reply(client, "Protected host patterns\n\n")
reply(client, "%-50s %-10s %-19s %s" % [ "Pattern", "By", "When", "Reason" ])
result = DB.execute(@dbq['GET_PROTECTED_PATTERNS_DETAILED'])
result.row_each { |row|
pattern = row[0]
by = row[1]
time = Time.at(row[2].to_i).strftime('%Y-%m-%d %H:%M:%S')
reason = row[3]
reply(client, "%-50s %-10s %-19s %s" % [ pattern, by, time, reason ])
}
result.free

reply(client, "\nCRFJ - checks Connect, Register nick, Join channel within 15 seconds (i.e. Fast)")
reply(client, "J - triggers on every Join")

Expand Down Expand Up @@ -574,8 +637,13 @@ def akill(client, reason, operreason, channel="")
ret = kill_user(client, reason)
else # if host
reason = "#{reason}|#{operreason}"
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
ret = akill_add("*@#{host}", reason, @akill_duration)
if client.host =~ /#{@protected_patterns}/i # if protected hosts
debug(LOG_DEBUG, "Using /kill instead of AKILL for protected user #{client.name}")
ret = kill_user(client, reason)
else
debug(LOG_DEBUG, "Issuing AKILL: *@#{host}, #{reason} lasting for #{@akill_duration} seconds")
ret = akill_add("*@#{host}", reason, @akill_duration)
end # if protected hosts
end # if host

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

# ------------------------------------------------------------------------

# convert irc pattern to regular expression
def irc_pattern_to_regex(pattern
# "." -> "\.", "*" -> ".*", "?" -> "."
# wrap with "^...$"
return pattern if pattern.start_with? '^'
pattern = pattern.gsub(/\./, '\\.')
.gsub(/\*/, '.*')
.gsub(/\?/, '.')
return "^#{pattern}$"
end

# ------------------------------------------------------------------------

# get protected patterns as pattern
def load_protected_patterns()
patterns = @protection.keys
if patterns.empty?
@protected_patterns = '^$'
else
@protected_patterns = patterns.join('|')
end
end # def get_protected_patterns

# ------------------------------------------------------------------------

# enforce a channel - kill all of its users
Expand Down Expand Up @@ -648,6 +741,20 @@ def load_data()
count += 1
}
result.free

@protection = Hash.new
result = DB.execute(@dbq['GET_PROTECTED_PATTERNS'], '')
count = 0
result.row_each { |row|
pattern = row[0]
reason = row[1]
pattern = irc_pattern_to_regex(pattern)
@protection[pattern] = reason
count += 1
}
result.free
load_protected_patterns

debug(LOG_DEBUG, "All channel data successfully loaded")
end # def load_data

Expand Down
10 changes: 10 additions & 0 deletions sql/ganneffserv-pgsql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,14 @@ CREATE TABLE ganneffserv (
kills INTEGER NOT NULL DEFAULT 0,
monitor_only BOOLEAN NOT NULL DEFAULT 'False'
);

DROP TABLE IF EXISTS ganneffprotect CASCADE;
CREATE TABLE ganneffprotect (
id SERIAL PRIMARY KEY,
setter INTEGER REFERENCES account(id) ON DELETE SET NULL,
time INTEGER NOT NULL,
pattern VARCHAR(255) NOT NULL,
reason VARCHAR(255) NOT NULL
)

CREATE UNIQUE INDEX ganneffserv_channel_idx ON ganneffserv (irc_lower(channel));

0 comments on commit 08ed923

Please sign in to comment.