Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GanneffServ: make it possible to not kill irccloud #50

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
115 changes: 112 additions & 3 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setter here will be an ID, not a name. Look at GET_ALL_CHANNELS for a simple way to get the primary nick of the setting account.

reason FROM ganneffprotect')
end # def initialize

########################################################################
Expand Down Expand Up @@ -221,6 +230,50 @@ 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the original pattern in the DB, rather than the regex. This is important for the listing later, as looking at IRC masks is nicer than regexes, unless we've specifically given a regex.

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
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
end

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

# List all channels we monitor
Expand All @@ -245,6 +298,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 @@ -326,7 +391,7 @@ def BADSERV(client, parv = [])
debug(LOG_DEBUG, "#{client.name} called BADSERV and the parms are #{parv.join(",")}")
server = parv[1].downcase

if server =~ /.*\.oftc.net$/
if server =~ /.*\.oftc\.net$/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unrelated, but included because I was thinking about regular expressions while working on this file. It's an outlier in this file, everything else included the \.

debug(LOG_DEBUG, "#{server} seems to be an oftc server, proceeding")
@badserver = server
reply(client, "#{server} is now marked as a bad server, all new connections will be killed")
Expand Down Expand Up @@ -574,8 +639,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaks the oper reason; kills don't hide oper reasons like akills do; easy fix is to just put this section as a branch in the enclosing if

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 +666,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 +743,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));
jsoref marked this conversation as resolved.
Show resolved Hide resolved