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