mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 10:42:52 +01:00
remove indirections in Fakelag and NickTimer
This commit is contained in:
parent
acd9eeeb15
commit
2e88f82e41
@ -57,7 +57,7 @@ type Client struct {
|
||||
channels ChannelSet
|
||||
ctime time.Time
|
||||
exitedSnomaskSent bool
|
||||
fakelag *Fakelag
|
||||
fakelag Fakelag
|
||||
flags *modes.ModeSet
|
||||
hasQuit bool
|
||||
hops int
|
||||
@ -75,7 +75,7 @@ type Client struct {
|
||||
nickCasefolded string
|
||||
nickMaskCasefolded string
|
||||
nickMaskString string // cache for nickmask string since it's used with lots of replies
|
||||
nickTimer *NickTimer
|
||||
nickTimer NickTimer
|
||||
oper *Oper
|
||||
preregNick string
|
||||
proxiedIP net.IP // actual remote IP if using the PROXY protocol
|
||||
@ -217,23 +217,9 @@ func (client *Client) isAuthorized(config *Config) bool {
|
||||
}
|
||||
|
||||
func (client *Client) resetFakelag() {
|
||||
fakelag := func() *Fakelag {
|
||||
if client.HasRoleCapabs("nofakelag") {
|
||||
return nil
|
||||
}
|
||||
|
||||
flc := client.server.FakelagConfig()
|
||||
|
||||
if !flc.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewFakelag(flc.Window, flc.BurstLimit, flc.MessagesPerWindow, flc.Cooldown)
|
||||
}()
|
||||
|
||||
client.stateMutex.Lock()
|
||||
defer client.stateMutex.Unlock()
|
||||
client.fakelag = fakelag
|
||||
var flc FakelagConfig = client.server.Config().Fakelag
|
||||
flc.Enabled = flc.Enabled && !client.HasRoleCapabs("nofakelag")
|
||||
client.fakelag.Initialize(flc)
|
||||
}
|
||||
|
||||
// IP returns the IP address of this client.
|
||||
@ -309,7 +295,7 @@ func (client *Client) run() {
|
||||
client.idletimer = NewIdleTimer(client)
|
||||
client.idletimer.Start()
|
||||
|
||||
client.nickTimer = NewNickTimer(client)
|
||||
client.nickTimer.Initialize(client)
|
||||
|
||||
client.resetFakelag()
|
||||
|
||||
|
@ -24,33 +24,25 @@ const (
|
||||
// this is intentionally not threadsafe, because it should only be touched
|
||||
// from the loop that accepts the client's input and runs commands
|
||||
type Fakelag struct {
|
||||
window time.Duration
|
||||
burstLimit uint
|
||||
throttleMessagesPerWindow uint
|
||||
cooldown time.Duration
|
||||
nowFunc func() time.Time
|
||||
sleepFunc func(time.Duration)
|
||||
config FakelagConfig
|
||||
nowFunc func() time.Time
|
||||
sleepFunc func(time.Duration)
|
||||
|
||||
state FakelagState
|
||||
burstCount uint // number of messages sent in the current burst
|
||||
lastTouch time.Time
|
||||
}
|
||||
|
||||
func NewFakelag(window time.Duration, burstLimit uint, throttleMessagesPerWindow uint, cooldown time.Duration) *Fakelag {
|
||||
return &Fakelag{
|
||||
window: window,
|
||||
burstLimit: burstLimit,
|
||||
throttleMessagesPerWindow: throttleMessagesPerWindow,
|
||||
cooldown: cooldown,
|
||||
nowFunc: time.Now,
|
||||
sleepFunc: time.Sleep,
|
||||
state: FakelagBursting,
|
||||
}
|
||||
func (fl *Fakelag) Initialize(config FakelagConfig) {
|
||||
fl.config = config
|
||||
fl.nowFunc = time.Now
|
||||
fl.sleepFunc = time.Sleep
|
||||
fl.state = FakelagBursting
|
||||
}
|
||||
|
||||
// register a new command, sleep if necessary to delay it
|
||||
func (fl *Fakelag) Touch() {
|
||||
if fl == nil {
|
||||
if !fl.config.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
@ -61,12 +53,12 @@ func (fl *Fakelag) Touch() {
|
||||
|
||||
if fl.state == FakelagBursting {
|
||||
// determine if the previous burst is over
|
||||
if elapsed > fl.cooldown {
|
||||
if elapsed > fl.config.Cooldown {
|
||||
fl.burstCount = 0
|
||||
}
|
||||
|
||||
fl.burstCount++
|
||||
if fl.burstCount > fl.burstLimit {
|
||||
if fl.burstCount > fl.config.BurstLimit {
|
||||
// reset burst window for next time
|
||||
fl.burstCount = 0
|
||||
// transition to throttling
|
||||
@ -78,13 +70,13 @@ func (fl *Fakelag) Touch() {
|
||||
}
|
||||
|
||||
if fl.state == FakelagThrottled {
|
||||
if elapsed > fl.cooldown {
|
||||
if elapsed > fl.config.Cooldown {
|
||||
// let them burst again
|
||||
fl.state = FakelagBursting
|
||||
return
|
||||
}
|
||||
// space them out by at least window/messagesperwindow
|
||||
sleepDuration := time.Duration((int64(fl.window) / int64(fl.throttleMessagesPerWindow)) - int64(elapsed))
|
||||
sleepDuration := time.Duration((int64(fl.config.Window) / int64(fl.config.MessagesPerWindow)) - int64(elapsed))
|
||||
if sleepDuration > 0 {
|
||||
fl.sleepFunc(sleepDuration)
|
||||
// the touch time should take into account the time we slept
|
||||
|
@ -40,13 +40,20 @@ func (mt *mockTime) lastSleep() (slept bool, duration time.Duration) {
|
||||
}
|
||||
|
||||
func newFakelagForTesting(window time.Duration, burstLimit uint, throttleMessagesPerWindow uint, cooldown time.Duration) (*Fakelag, *mockTime) {
|
||||
fl := NewFakelag(window, burstLimit, throttleMessagesPerWindow, cooldown)
|
||||
fl := Fakelag{}
|
||||
fl.config = FakelagConfig{
|
||||
Enabled: true,
|
||||
Window: window,
|
||||
BurstLimit: burstLimit,
|
||||
MessagesPerWindow: throttleMessagesPerWindow,
|
||||
Cooldown: cooldown,
|
||||
}
|
||||
mt := new(mockTime)
|
||||
mt.now, _ = time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Mon Jan 2 15:04:05 -0700 MST 2006")
|
||||
mt.lastCheckedSleep = -1
|
||||
fl.nowFunc = mt.Now
|
||||
fl.sleepFunc = mt.Sleep
|
||||
return fl, mt
|
||||
return &fl, mt
|
||||
}
|
||||
|
||||
func TestFakelag(t *testing.T) {
|
||||
|
@ -46,10 +46,6 @@ func (server *Server) AccountConfig() *AccountConfig {
|
||||
return &server.Config().Accounts
|
||||
}
|
||||
|
||||
func (server *Server) FakelagConfig() *FakelagConfig {
|
||||
return &server.Config().Fakelag
|
||||
}
|
||||
|
||||
func (server *Server) GetOperator(name string) (oper *Oper) {
|
||||
name, err := CasefoldName(name)
|
||||
if err != nil {
|
||||
|
@ -6,6 +6,7 @@ package irc
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/goshuirc/irc-go/ircfmt"
|
||||
@ -180,33 +181,50 @@ type NickTimer struct {
|
||||
sync.Mutex // tier 1
|
||||
|
||||
// immutable after construction
|
||||
timeout time.Duration
|
||||
client *Client
|
||||
client *Client
|
||||
|
||||
// mutable
|
||||
stopped bool
|
||||
nick string
|
||||
accountForNick string
|
||||
account string
|
||||
timeout time.Duration
|
||||
timer *time.Timer
|
||||
enabled uint32
|
||||
}
|
||||
|
||||
// NewNickTimer sets up a new nick timer (returning nil if timeout enforcement is not enabled)
|
||||
func NewNickTimer(client *Client) *NickTimer {
|
||||
config := client.server.AccountConfig().NickReservation
|
||||
if !(config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)) {
|
||||
return nil
|
||||
// Initialize sets up a NickTimer, based on server config settings.
|
||||
func (nt *NickTimer) Initialize(client *Client) {
|
||||
if nt.client == nil {
|
||||
nt.client = client // placate the race detector
|
||||
}
|
||||
|
||||
return &NickTimer{
|
||||
client: client,
|
||||
timeout: config.RenameTimeout,
|
||||
config := &client.server.Config().Accounts.NickReservation
|
||||
enabled := config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)
|
||||
|
||||
nt.Lock()
|
||||
defer nt.Unlock()
|
||||
nt.timeout = config.RenameTimeout
|
||||
if enabled {
|
||||
atomic.StoreUint32(&nt.enabled, 1)
|
||||
} else {
|
||||
nt.stopInternal()
|
||||
}
|
||||
}
|
||||
|
||||
func (nt *NickTimer) Enabled() bool {
|
||||
return atomic.LoadUint32(&nt.enabled) == 1
|
||||
}
|
||||
|
||||
func (nt *NickTimer) Timeout() (timeout time.Duration) {
|
||||
nt.Lock()
|
||||
timeout = nt.timeout
|
||||
nt.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Touch records a nick change and updates the timer as necessary
|
||||
func (nt *NickTimer) Touch() {
|
||||
if nt == nil {
|
||||
if !nt.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
@ -215,16 +233,12 @@ func (nt *NickTimer) Touch() {
|
||||
accountForNick, method := nt.client.server.accounts.EnforcementStatus(cfnick, skeleton)
|
||||
enforceTimeout := method == NickReservationWithTimeout
|
||||
|
||||
var shouldWarn bool
|
||||
var shouldWarn, shouldRename bool
|
||||
|
||||
func() {
|
||||
nt.Lock()
|
||||
defer nt.Unlock()
|
||||
|
||||
if nt.stopped {
|
||||
return
|
||||
}
|
||||
|
||||
// the timer will not reset as long as the squatter is targeting the same account
|
||||
accountChanged := accountForNick != nt.accountForNick
|
||||
// change state
|
||||
@ -237,38 +251,39 @@ func (nt *NickTimer) Touch() {
|
||||
nt.timer.Stop()
|
||||
nt.timer = nil
|
||||
}
|
||||
if enforceTimeout && delinquent && accountChanged {
|
||||
if enforceTimeout && delinquent && (accountChanged || nt.timer == nil) {
|
||||
nt.timer = time.AfterFunc(nt.timeout, nt.processTimeout)
|
||||
shouldWarn = true
|
||||
} else if method == NickReservationStrict && delinquent {
|
||||
shouldRename = true // this can happen if reservation was enabled by rehash
|
||||
}
|
||||
}()
|
||||
|
||||
if shouldWarn {
|
||||
nt.sendWarning()
|
||||
nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.Timeout()))
|
||||
} else if shouldRename {
|
||||
nt.client.Notice(nt.client.t("Nickname is reserved by a different account"))
|
||||
nt.client.server.RandomlyRename(nt.client)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops counting time and cleans up the timer
|
||||
func (nt *NickTimer) Stop() {
|
||||
if nt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
nt.Lock()
|
||||
defer nt.Unlock()
|
||||
nt.stopInternal()
|
||||
}
|
||||
|
||||
func (nt *NickTimer) stopInternal() {
|
||||
if nt.timer != nil {
|
||||
nt.timer.Stop()
|
||||
nt.timer = nil
|
||||
}
|
||||
nt.stopped = true
|
||||
}
|
||||
|
||||
func (nt *NickTimer) sendWarning() {
|
||||
nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.timeout))
|
||||
atomic.StoreUint32(&nt.enabled, 0)
|
||||
}
|
||||
|
||||
func (nt *NickTimer) processTimeout() {
|
||||
baseMsg := "Nick is reserved and authentication timeout expired: %v"
|
||||
nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
|
||||
nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.Timeout()))
|
||||
nt.client.server.RandomlyRename(nt.client)
|
||||
}
|
||||
|
@ -826,6 +826,13 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
|
||||
if sendRawOutputNotice {
|
||||
sClient.Notice(sClient.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
|
||||
}
|
||||
|
||||
if !oldConfig.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.Enabled {
|
||||
sClient.nickTimer.Initialize(sClient)
|
||||
sClient.nickTimer.Touch()
|
||||
} else if oldConfig.Accounts.NickReservation.Enabled && !config.Accounts.NickReservation.Enabled {
|
||||
sClient.nickTimer.Stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user