Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

idletimer.go 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "sync"
  7. "time"
  8. "github.com/goshuirc/irc-go/ircfmt"
  9. "github.com/oragono/oragono/irc/caps"
  10. )
  11. const (
  12. // RegisterTimeout is how long clients have to register before we disconnect them
  13. RegisterTimeout = time.Minute
  14. // IdleTimeout is how long without traffic before a registered client is considered idle.
  15. IdleTimeout = time.Minute + time.Second*30
  16. // IdleTimeoutWithResumeCap is how long without traffic before a registered client is considered idle, when they have the resume capability.
  17. IdleTimeoutWithResumeCap = time.Minute*2 + time.Second*30
  18. // QuitTimeout is how long without traffic before an idle client is disconnected
  19. QuitTimeout = time.Minute
  20. )
  21. // client idleness state machine
  22. type TimerState uint
  23. const (
  24. TimerUnregistered TimerState = iota // client is unregistered
  25. TimerActive // client is actively sending commands
  26. TimerIdle // client is idle, we sent PING and are waiting for PONG
  27. TimerDead // client was terminated
  28. )
  29. type IdleTimer struct {
  30. sync.Mutex // tier 1
  31. // immutable after construction
  32. registerTimeout time.Duration
  33. quitTimeout time.Duration
  34. client *Client
  35. // mutable
  36. idleTimeout time.Duration
  37. state TimerState
  38. timer *time.Timer
  39. }
  40. // NewIdleTimer sets up a new IdleTimer using constant timeouts.
  41. func NewIdleTimer(client *Client) *IdleTimer {
  42. it := IdleTimer{
  43. registerTimeout: RegisterTimeout,
  44. idleTimeout: IdleTimeout,
  45. quitTimeout: QuitTimeout,
  46. client: client,
  47. }
  48. return &it
  49. }
  50. // updateIdleDuration updates the idle duration, given the client's caps.
  51. func (it *IdleTimer) updateIdleDuration() {
  52. newIdleTime := IdleTimeout
  53. // if they have the resume cap, wait longer before pinging them out
  54. // to give them a chance to resume their connection
  55. if it.client.capabilities.Has(caps.Resume) {
  56. newIdleTime = IdleTimeoutWithResumeCap
  57. }
  58. it.Lock()
  59. defer it.Unlock()
  60. it.idleTimeout = newIdleTime
  61. }
  62. // Start starts counting idle time; if there is no activity from the client,
  63. // it will eventually be stopped.
  64. func (it *IdleTimer) Start() {
  65. it.Lock()
  66. defer it.Unlock()
  67. it.state = TimerUnregistered
  68. it.resetTimeout()
  69. }
  70. func (it *IdleTimer) Touch() {
  71. it.updateIdleDuration()
  72. it.Lock()
  73. defer it.Unlock()
  74. // a touch transitions TimerUnregistered or TimerIdle into TimerActive
  75. if it.state != TimerDead {
  76. it.state = TimerActive
  77. it.resetTimeout()
  78. }
  79. }
  80. func (it *IdleTimer) processTimeout() {
  81. it.updateIdleDuration()
  82. var previousState TimerState
  83. func() {
  84. it.Lock()
  85. defer it.Unlock()
  86. previousState = it.state
  87. // TimerActive transitions to TimerIdle, all others to TimerDead
  88. if it.state == TimerActive {
  89. // send them a ping, give them time to respond
  90. it.state = TimerIdle
  91. it.resetTimeout()
  92. } else {
  93. it.state = TimerDead
  94. }
  95. }()
  96. if previousState == TimerActive {
  97. it.client.Ping()
  98. } else {
  99. it.client.Quit(it.quitMessage(previousState))
  100. it.client.destroy(false)
  101. }
  102. }
  103. // Stop stops counting idle time.
  104. func (it *IdleTimer) Stop() {
  105. if it == nil {
  106. return
  107. }
  108. it.Lock()
  109. defer it.Unlock()
  110. it.state = TimerDead
  111. it.resetTimeout()
  112. }
  113. func (it *IdleTimer) resetTimeout() {
  114. if it.timer != nil {
  115. it.timer.Stop()
  116. }
  117. var nextTimeout time.Duration
  118. switch it.state {
  119. case TimerUnregistered:
  120. nextTimeout = it.registerTimeout
  121. case TimerActive:
  122. nextTimeout = it.idleTimeout
  123. case TimerIdle:
  124. nextTimeout = it.quitTimeout
  125. case TimerDead:
  126. return
  127. }
  128. it.timer = time.AfterFunc(nextTimeout, it.processTimeout)
  129. }
  130. func (it *IdleTimer) quitMessage(state TimerState) string {
  131. switch state {
  132. case TimerUnregistered:
  133. return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
  134. case TimerIdle:
  135. // how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
  136. it.Lock()
  137. defer it.Unlock()
  138. return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
  139. default:
  140. // shouldn't happen
  141. return ""
  142. }
  143. }
  144. // NickTimer manages timing out of clients who are squatting reserved nicks
  145. type NickTimer struct {
  146. sync.Mutex // tier 1
  147. // immutable after construction
  148. timeout time.Duration
  149. client *Client
  150. // mutable
  151. stopped bool
  152. nick string
  153. accountForNick string
  154. account string
  155. timer *time.Timer
  156. }
  157. // NewNickTimer sets up a new nick timer (returning nil if timeout enforcement is not enabled)
  158. func NewNickTimer(client *Client) *NickTimer {
  159. config := client.server.AccountConfig().NickReservation
  160. if !(config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)) {
  161. return nil
  162. }
  163. return &NickTimer{
  164. client: client,
  165. timeout: config.RenameTimeout,
  166. }
  167. }
  168. // Touch records a nick change and updates the timer as necessary
  169. func (nt *NickTimer) Touch() {
  170. if nt == nil {
  171. return
  172. }
  173. cfnick, skeleton := nt.client.uniqueIdentifiers()
  174. account := nt.client.Account()
  175. accountForNick, method := nt.client.server.accounts.EnforcementStatus(cfnick, skeleton)
  176. enforceTimeout := method == NickReservationWithTimeout
  177. var shouldWarn bool
  178. func() {
  179. nt.Lock()
  180. defer nt.Unlock()
  181. if nt.stopped {
  182. return
  183. }
  184. // the timer will not reset as long as the squatter is targeting the same account
  185. accountChanged := accountForNick != nt.accountForNick
  186. // change state
  187. nt.nick = cfnick
  188. nt.account = account
  189. nt.accountForNick = accountForNick
  190. delinquent := accountForNick != "" && accountForNick != account
  191. if nt.timer != nil && (!enforceTimeout || !delinquent || accountChanged) {
  192. nt.timer.Stop()
  193. nt.timer = nil
  194. }
  195. if enforceTimeout && delinquent && accountChanged {
  196. nt.timer = time.AfterFunc(nt.timeout, nt.processTimeout)
  197. shouldWarn = true
  198. }
  199. }()
  200. if shouldWarn {
  201. nt.sendWarning()
  202. }
  203. }
  204. // Stop stops counting time and cleans up the timer
  205. func (nt *NickTimer) Stop() {
  206. if nt == nil {
  207. return
  208. }
  209. nt.Lock()
  210. defer nt.Unlock()
  211. if nt.timer != nil {
  212. nt.timer.Stop()
  213. nt.timer = nil
  214. }
  215. nt.stopped = true
  216. }
  217. func (nt *NickTimer) sendWarning() {
  218. nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.timeout))
  219. }
  220. func (nt *NickTimer) processTimeout() {
  221. baseMsg := "Nick is reserved and authentication timeout expired: %v"
  222. nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.timeout))
  223. nt.client.server.RandomlyRename(nt.client)
  224. }