Browse Source

Merge pull request #443 from slingamn/remove_indirections.4

remove indirections in Fakelag and NickTimer
tags/v1.1.0-rc1
Daniel Oaks 5 years ago
parent
commit
51f55ae44e
No account linked to committer's email address
6 changed files with 79 additions and 76 deletions
  1. 6
    20
      irc/client.go
  2. 13
    21
      irc/fakelag.go
  3. 9
    2
      irc/fakelag_test.go
  4. 0
    4
      irc/getters.go
  5. 44
    29
      irc/idletimer.go
  6. 7
    0
      irc/server.go

+ 6
- 20
irc/client.go View File

57
 	channels           ChannelSet
57
 	channels           ChannelSet
58
 	ctime              time.Time
58
 	ctime              time.Time
59
 	exitedSnomaskSent  bool
59
 	exitedSnomaskSent  bool
60
-	fakelag            *Fakelag
60
+	fakelag            Fakelag
61
 	flags              *modes.ModeSet
61
 	flags              *modes.ModeSet
62
 	hasQuit            bool
62
 	hasQuit            bool
63
 	hops               int
63
 	hops               int
75
 	nickCasefolded     string
75
 	nickCasefolded     string
76
 	nickMaskCasefolded string
76
 	nickMaskCasefolded string
77
 	nickMaskString     string // cache for nickmask string since it's used with lots of replies
77
 	nickMaskString     string // cache for nickmask string since it's used with lots of replies
78
-	nickTimer          *NickTimer
78
+	nickTimer          NickTimer
79
 	oper               *Oper
79
 	oper               *Oper
80
 	preregNick         string
80
 	preregNick         string
81
 	proxiedIP          net.IP // actual remote IP if using the PROXY protocol
81
 	proxiedIP          net.IP // actual remote IP if using the PROXY protocol
217
 }
217
 }
218
 
218
 
219
 func (client *Client) resetFakelag() {
219
 func (client *Client) resetFakelag() {
220
-	fakelag := func() *Fakelag {
221
-		if client.HasRoleCapabs("nofakelag") {
222
-			return nil
223
-		}
224
-
225
-		flc := client.server.FakelagConfig()
226
-
227
-		if !flc.Enabled {
228
-			return nil
229
-		}
230
-
231
-		return NewFakelag(flc.Window, flc.BurstLimit, flc.MessagesPerWindow, flc.Cooldown)
232
-	}()
233
-
234
-	client.stateMutex.Lock()
235
-	defer client.stateMutex.Unlock()
236
-	client.fakelag = fakelag
220
+	var flc FakelagConfig = client.server.Config().Fakelag
221
+	flc.Enabled = flc.Enabled && !client.HasRoleCapabs("nofakelag")
222
+	client.fakelag.Initialize(flc)
237
 }
223
 }
238
 
224
 
239
 // IP returns the IP address of this client.
225
 // IP returns the IP address of this client.
309
 	client.idletimer = NewIdleTimer(client)
295
 	client.idletimer = NewIdleTimer(client)
310
 	client.idletimer.Start()
296
 	client.idletimer.Start()
311
 
297
 
312
-	client.nickTimer = NewNickTimer(client)
298
+	client.nickTimer.Initialize(client)
313
 
299
 
314
 	client.resetFakelag()
300
 	client.resetFakelag()
315
 
301
 

+ 13
- 21
irc/fakelag.go View File

24
 // this is intentionally not threadsafe, because it should only be touched
24
 // this is intentionally not threadsafe, because it should only be touched
25
 // from the loop that accepts the client's input and runs commands
25
 // from the loop that accepts the client's input and runs commands
26
 type Fakelag struct {
26
 type Fakelag struct {
27
-	window                    time.Duration
28
-	burstLimit                uint
29
-	throttleMessagesPerWindow uint
30
-	cooldown                  time.Duration
31
-	nowFunc                   func() time.Time
32
-	sleepFunc                 func(time.Duration)
27
+	config    FakelagConfig
28
+	nowFunc   func() time.Time
29
+	sleepFunc func(time.Duration)
33
 
30
 
34
 	state      FakelagState
31
 	state      FakelagState
35
 	burstCount uint // number of messages sent in the current burst
32
 	burstCount uint // number of messages sent in the current burst
36
 	lastTouch  time.Time
33
 	lastTouch  time.Time
37
 }
34
 }
38
 
35
 
39
-func NewFakelag(window time.Duration, burstLimit uint, throttleMessagesPerWindow uint, cooldown time.Duration) *Fakelag {
40
-	return &Fakelag{
41
-		window:                    window,
42
-		burstLimit:                burstLimit,
43
-		throttleMessagesPerWindow: throttleMessagesPerWindow,
44
-		cooldown:                  cooldown,
45
-		nowFunc:                   time.Now,
46
-		sleepFunc:                 time.Sleep,
47
-		state:                     FakelagBursting,
48
-	}
36
+func (fl *Fakelag) Initialize(config FakelagConfig) {
37
+	fl.config = config
38
+	fl.nowFunc = time.Now
39
+	fl.sleepFunc = time.Sleep
40
+	fl.state = FakelagBursting
49
 }
41
 }
50
 
42
 
51
 // register a new command, sleep if necessary to delay it
43
 // register a new command, sleep if necessary to delay it
52
 func (fl *Fakelag) Touch() {
44
 func (fl *Fakelag) Touch() {
53
-	if fl == nil {
45
+	if !fl.config.Enabled {
54
 		return
46
 		return
55
 	}
47
 	}
56
 
48
 
61
 
53
 
62
 	if fl.state == FakelagBursting {
54
 	if fl.state == FakelagBursting {
63
 		// determine if the previous burst is over
55
 		// determine if the previous burst is over
64
-		if elapsed > fl.cooldown {
56
+		if elapsed > fl.config.Cooldown {
65
 			fl.burstCount = 0
57
 			fl.burstCount = 0
66
 		}
58
 		}
67
 
59
 
68
 		fl.burstCount++
60
 		fl.burstCount++
69
-		if fl.burstCount > fl.burstLimit {
61
+		if fl.burstCount > fl.config.BurstLimit {
70
 			// reset burst window for next time
62
 			// reset burst window for next time
71
 			fl.burstCount = 0
63
 			fl.burstCount = 0
72
 			// transition to throttling
64
 			// transition to throttling
78
 	}
70
 	}
79
 
71
 
80
 	if fl.state == FakelagThrottled {
72
 	if fl.state == FakelagThrottled {
81
-		if elapsed > fl.cooldown {
73
+		if elapsed > fl.config.Cooldown {
82
 			// let them burst again
74
 			// let them burst again
83
 			fl.state = FakelagBursting
75
 			fl.state = FakelagBursting
84
 			return
76
 			return
85
 		}
77
 		}
86
 		// space them out by at least window/messagesperwindow
78
 		// space them out by at least window/messagesperwindow
87
-		sleepDuration := time.Duration((int64(fl.window) / int64(fl.throttleMessagesPerWindow)) - int64(elapsed))
79
+		sleepDuration := time.Duration((int64(fl.config.Window) / int64(fl.config.MessagesPerWindow)) - int64(elapsed))
88
 		if sleepDuration > 0 {
80
 		if sleepDuration > 0 {
89
 			fl.sleepFunc(sleepDuration)
81
 			fl.sleepFunc(sleepDuration)
90
 			// the touch time should take into account the time we slept
82
 			// the touch time should take into account the time we slept

+ 9
- 2
irc/fakelag_test.go View File

40
 }
40
 }
41
 
41
 
42
 func newFakelagForTesting(window time.Duration, burstLimit uint, throttleMessagesPerWindow uint, cooldown time.Duration) (*Fakelag, *mockTime) {
42
 func newFakelagForTesting(window time.Duration, burstLimit uint, throttleMessagesPerWindow uint, cooldown time.Duration) (*Fakelag, *mockTime) {
43
-	fl := NewFakelag(window, burstLimit, throttleMessagesPerWindow, cooldown)
43
+	fl := Fakelag{}
44
+	fl.config = FakelagConfig{
45
+		Enabled:           true,
46
+		Window:            window,
47
+		BurstLimit:        burstLimit,
48
+		MessagesPerWindow: throttleMessagesPerWindow,
49
+		Cooldown:          cooldown,
50
+	}
44
 	mt := new(mockTime)
51
 	mt := new(mockTime)
45
 	mt.now, _ = time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Mon Jan 2 15:04:05 -0700 MST 2006")
52
 	mt.now, _ = time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Mon Jan 2 15:04:05 -0700 MST 2006")
46
 	mt.lastCheckedSleep = -1
53
 	mt.lastCheckedSleep = -1
47
 	fl.nowFunc = mt.Now
54
 	fl.nowFunc = mt.Now
48
 	fl.sleepFunc = mt.Sleep
55
 	fl.sleepFunc = mt.Sleep
49
-	return fl, mt
56
+	return &fl, mt
50
 }
57
 }
51
 
58
 
52
 func TestFakelag(t *testing.T) {
59
 func TestFakelag(t *testing.T) {

+ 0
- 4
irc/getters.go View File

46
 	return &server.Config().Accounts
46
 	return &server.Config().Accounts
47
 }
47
 }
48
 
48
 
49
-func (server *Server) FakelagConfig() *FakelagConfig {
50
-	return &server.Config().Fakelag
51
-}
52
-
53
 func (server *Server) GetOperator(name string) (oper *Oper) {
49
 func (server *Server) GetOperator(name string) (oper *Oper) {
54
 	name, err := CasefoldName(name)
50
 	name, err := CasefoldName(name)
55
 	if err != nil {
51
 	if err != nil {

+ 44
- 29
irc/idletimer.go View File

6
 import (
6
 import (
7
 	"fmt"
7
 	"fmt"
8
 	"sync"
8
 	"sync"
9
+	"sync/atomic"
9
 	"time"
10
 	"time"
10
 
11
 
11
 	"github.com/goshuirc/irc-go/ircfmt"
12
 	"github.com/goshuirc/irc-go/ircfmt"
180
 	sync.Mutex // tier 1
181
 	sync.Mutex // tier 1
181
 
182
 
182
 	// immutable after construction
183
 	// immutable after construction
183
-	timeout time.Duration
184
-	client  *Client
184
+	client *Client
185
 
185
 
186
 	// mutable
186
 	// mutable
187
-	stopped        bool
188
 	nick           string
187
 	nick           string
189
 	accountForNick string
188
 	accountForNick string
190
 	account        string
189
 	account        string
190
+	timeout        time.Duration
191
 	timer          *time.Timer
191
 	timer          *time.Timer
192
+	enabled        uint32
192
 }
193
 }
193
 
194
 
194
-// NewNickTimer sets up a new nick timer (returning nil if timeout enforcement is not enabled)
195
-func NewNickTimer(client *Client) *NickTimer {
196
-	config := client.server.AccountConfig().NickReservation
197
-	if !(config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)) {
198
-		return nil
195
+// Initialize sets up a NickTimer, based on server config settings.
196
+func (nt *NickTimer) Initialize(client *Client) {
197
+	if nt.client == nil {
198
+		nt.client = client // placate the race detector
199
 	}
199
 	}
200
 
200
 
201
-	return &NickTimer{
202
-		client:  client,
203
-		timeout: config.RenameTimeout,
201
+	config := &client.server.Config().Accounts.NickReservation
202
+	enabled := config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)
203
+
204
+	nt.Lock()
205
+	defer nt.Unlock()
206
+	nt.timeout = config.RenameTimeout
207
+	if enabled {
208
+		atomic.StoreUint32(&nt.enabled, 1)
209
+	} else {
210
+		nt.stopInternal()
204
 	}
211
 	}
205
 }
212
 }
206
 
213
 
214
+func (nt *NickTimer) Enabled() bool {
215
+	return atomic.LoadUint32(&nt.enabled) == 1
216
+}
217
+
218
+func (nt *NickTimer) Timeout() (timeout time.Duration) {
219
+	nt.Lock()
220
+	timeout = nt.timeout
221
+	nt.Unlock()
222
+	return
223
+}
224
+
207
 // Touch records a nick change and updates the timer as necessary
225
 // Touch records a nick change and updates the timer as necessary
208
 func (nt *NickTimer) Touch() {
226
 func (nt *NickTimer) Touch() {
209
-	if nt == nil {
227
+	if !nt.Enabled() {
210
 		return
228
 		return
211
 	}
229
 	}
212
 
230
 
215
 	accountForNick, method := nt.client.server.accounts.EnforcementStatus(cfnick, skeleton)
233
 	accountForNick, method := nt.client.server.accounts.EnforcementStatus(cfnick, skeleton)
216
 	enforceTimeout := method == NickReservationWithTimeout
234
 	enforceTimeout := method == NickReservationWithTimeout
217
 
235
 
218
-	var shouldWarn bool
236
+	var shouldWarn, shouldRename bool
219
 
237
 
220
 	func() {
238
 	func() {
221
 		nt.Lock()
239
 		nt.Lock()
222
 		defer nt.Unlock()
240
 		defer nt.Unlock()
223
 
241
 
224
-		if nt.stopped {
225
-			return
226
-		}
227
-
228
 		// the timer will not reset as long as the squatter is targeting the same account
242
 		// the timer will not reset as long as the squatter is targeting the same account
229
 		accountChanged := accountForNick != nt.accountForNick
243
 		accountChanged := accountForNick != nt.accountForNick
230
 		// change state
244
 		// change state
237
 			nt.timer.Stop()
251
 			nt.timer.Stop()
238
 			nt.timer = nil
252
 			nt.timer = nil
239
 		}
253
 		}
240
-		if enforceTimeout && delinquent && accountChanged {
254
+		if enforceTimeout && delinquent && (accountChanged || nt.timer == nil) {
241
 			nt.timer = time.AfterFunc(nt.timeout, nt.processTimeout)
255
 			nt.timer = time.AfterFunc(nt.timeout, nt.processTimeout)
242
 			shouldWarn = true
256
 			shouldWarn = true
257
+		} else if method == NickReservationStrict && delinquent {
258
+			shouldRename = true // this can happen if reservation was enabled by rehash
243
 		}
259
 		}
244
 	}()
260
 	}()
245
 
261
 
246
 	if shouldWarn {
262
 	if shouldWarn {
247
-		nt.sendWarning()
263
+		nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.Timeout()))
264
+	} else if shouldRename {
265
+		nt.client.Notice(nt.client.t("Nickname is reserved by a different account"))
266
+		nt.client.server.RandomlyRename(nt.client)
248
 	}
267
 	}
249
 }
268
 }
250
 
269
 
251
 // Stop stops counting time and cleans up the timer
270
 // Stop stops counting time and cleans up the timer
252
 func (nt *NickTimer) Stop() {
271
 func (nt *NickTimer) Stop() {
253
-	if nt == nil {
254
-		return
255
-	}
256
-
257
 	nt.Lock()
272
 	nt.Lock()
258
 	defer nt.Unlock()
273
 	defer nt.Unlock()
274
+	nt.stopInternal()
275
+}
276
+
277
+func (nt *NickTimer) stopInternal() {
259
 	if nt.timer != nil {
278
 	if nt.timer != nil {
260
 		nt.timer.Stop()
279
 		nt.timer.Stop()
261
 		nt.timer = nil
280
 		nt.timer = nil
262
 	}
281
 	}
263
-	nt.stopped = true
264
-}
265
-
266
-func (nt *NickTimer) sendWarning() {
267
-	nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.timeout))
282
+	atomic.StoreUint32(&nt.enabled, 0)
268
 }
283
 }
269
 
284
 
270
 func (nt *NickTimer) processTimeout() {
285
 func (nt *NickTimer) processTimeout() {
271
 	baseMsg := "Nick is reserved and authentication timeout expired: %v"
286
 	baseMsg := "Nick is reserved and authentication timeout expired: %v"
272
-	nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
287
+	nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.Timeout()))
273
 	nt.client.server.RandomlyRename(nt.client)
288
 	nt.client.server.RandomlyRename(nt.client)
274
 }
289
 }

+ 7
- 0
irc/server.go View File

826
 			if sendRawOutputNotice {
826
 			if sendRawOutputNotice {
827
 				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."))
827
 				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."))
828
 			}
828
 			}
829
+
830
+			if !oldConfig.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.Enabled {
831
+				sClient.nickTimer.Initialize(sClient)
832
+				sClient.nickTimer.Touch()
833
+			} else if oldConfig.Accounts.NickReservation.Enabled && !config.Accounts.NickReservation.Enabled {
834
+				sClient.nickTimer.Stop()
835
+			}
829
 		}
836
 		}
830
 	}
837
 	}
831
 
838
 

Loading…
Cancel
Save