Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

idletimer.go 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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. "sync/atomic"
  8. "time"
  9. "github.com/goshuirc/irc-go/ircfmt"
  10. "github.com/oragono/oragono/irc/caps"
  11. )
  12. const (
  13. // RegisterTimeout is how long clients have to register before we disconnect them
  14. RegisterTimeout = time.Minute
  15. // DefaultIdleTimeout is how long without traffic before we send the client a PING
  16. DefaultIdleTimeout = time.Minute + 30*time.Second
  17. // For Tor clients, we send a PING at least every 30 seconds, as a workaround for this bug
  18. // (single-onion circuits will close unless the client sends data once every 60 seconds):
  19. // https://bugs.torproject.org/29665
  20. TorIdleTimeout = time.Second * 30
  21. // This is how long a client gets without sending any message, including the PONG to our
  22. // PING, before we disconnect them:
  23. DefaultTotalTimeout = 2*time.Minute + 30*time.Second
  24. // Resumeable clients (clients who have negotiated caps.Resume) get longer:
  25. ResumeableTotalTimeout = 3*time.Minute + 30*time.Second
  26. )
  27. // client idleness state machine
  28. type TimerState uint
  29. const (
  30. TimerUnregistered TimerState = iota // client is unregistered
  31. TimerActive // client is actively sending commands
  32. TimerIdle // client is idle, we sent PING and are waiting for PONG
  33. TimerDead // client was terminated
  34. )
  35. type IdleTimer struct {
  36. sync.Mutex // tier 1
  37. // immutable after construction
  38. registerTimeout time.Duration
  39. client *Client
  40. // mutable
  41. idleTimeout time.Duration
  42. quitTimeout time.Duration
  43. state TimerState
  44. timer *time.Timer
  45. }
  46. // NewIdleTimer sets up a new IdleTimer using constant timeouts.
  47. func NewIdleTimer(client *Client) *IdleTimer {
  48. it := IdleTimer{
  49. registerTimeout: RegisterTimeout,
  50. client: client,
  51. }
  52. it.idleTimeout, it.quitTimeout = it.recomputeDurations()
  53. return &it
  54. }
  55. // recomputeDurations recomputes the idle and quit durations, given the client's caps.
  56. func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duration) {
  57. totalTimeout := DefaultTotalTimeout
  58. // if they have the resume cap, wait longer before pinging them out
  59. // to give them a chance to resume their connection
  60. if it.client.capabilities.Has(caps.Resume) {
  61. totalTimeout = ResumeableTotalTimeout
  62. }
  63. idleTimeout = DefaultIdleTimeout
  64. if it.client.isTor {
  65. idleTimeout = TorIdleTimeout
  66. }
  67. quitTimeout = totalTimeout - idleTimeout
  68. return
  69. }
  70. // Start starts counting idle time; if there is no activity from the client,
  71. // it will eventually be stopped.
  72. func (it *IdleTimer) Start() {
  73. it.Lock()
  74. defer it.Unlock()
  75. it.state = TimerUnregistered
  76. it.resetTimeout()
  77. }
  78. func (it *IdleTimer) Touch() {
  79. idleTimeout, quitTimeout := it.recomputeDurations()
  80. it.Lock()
  81. defer it.Unlock()
  82. it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
  83. // a touch transitions TimerUnregistered or TimerIdle into TimerActive
  84. if it.state != TimerDead {
  85. it.state = TimerActive
  86. it.resetTimeout()
  87. }
  88. }
  89. func (it *IdleTimer) processTimeout() {
  90. idleTimeout, quitTimeout := it.recomputeDurations()
  91. var previousState TimerState
  92. func() {
  93. it.Lock()
  94. defer it.Unlock()
  95. it.idleTimeout, it.quitTimeout = idleTimeout, quitTimeout
  96. previousState = it.state
  97. // TimerActive transitions to TimerIdle, all others to TimerDead
  98. if it.state == TimerActive {
  99. // send them a ping, give them time to respond
  100. it.state = TimerIdle
  101. it.resetTimeout()
  102. } else {
  103. it.state = TimerDead
  104. }
  105. }()
  106. if previousState == TimerActive {
  107. it.client.Ping()
  108. } else {
  109. it.client.Quit(it.quitMessage(previousState))
  110. it.client.destroy(false)
  111. }
  112. }
  113. // Stop stops counting idle time.
  114. func (it *IdleTimer) Stop() {
  115. if it == nil {
  116. return
  117. }
  118. it.Lock()
  119. defer it.Unlock()
  120. it.state = TimerDead
  121. it.resetTimeout()
  122. }
  123. func (it *IdleTimer) resetTimeout() {
  124. if it.timer != nil {
  125. it.timer.Stop()
  126. }
  127. var nextTimeout time.Duration
  128. switch it.state {
  129. case TimerUnregistered:
  130. nextTimeout = it.registerTimeout
  131. case TimerActive:
  132. nextTimeout = it.idleTimeout
  133. case TimerIdle:
  134. nextTimeout = it.quitTimeout
  135. case TimerDead:
  136. return
  137. }
  138. it.timer = time.AfterFunc(nextTimeout, it.processTimeout)
  139. }
  140. func (it *IdleTimer) quitMessage(state TimerState) string {
  141. switch state {
  142. case TimerUnregistered:
  143. return fmt.Sprintf("Registration timeout: %v", it.registerTimeout)
  144. case TimerIdle:
  145. // how many seconds before registered clients are timed out (IdleTimeout plus QuitTimeout).
  146. it.Lock()
  147. defer it.Unlock()
  148. return fmt.Sprintf("Ping timeout: %v", (it.idleTimeout + it.quitTimeout))
  149. default:
  150. // shouldn't happen
  151. return ""
  152. }
  153. }
  154. // NickTimer manages timing out of clients who are squatting reserved nicks
  155. type NickTimer struct {
  156. sync.Mutex // tier 1
  157. // immutable after construction
  158. client *Client
  159. // mutable
  160. nick string
  161. accountForNick string
  162. account string
  163. timeout time.Duration
  164. timer *time.Timer
  165. enabled uint32
  166. }
  167. // Initialize sets up a NickTimer, based on server config settings.
  168. func (nt *NickTimer) Initialize(client *Client) {
  169. if nt.client == nil {
  170. nt.client = client // placate the race detector
  171. }
  172. config := &client.server.Config().Accounts.NickReservation
  173. enabled := config.Enabled && (config.Method == NickReservationWithTimeout || config.AllowCustomEnforcement)
  174. nt.Lock()
  175. defer nt.Unlock()
  176. nt.timeout = config.RenameTimeout
  177. if enabled {
  178. atomic.StoreUint32(&nt.enabled, 1)
  179. } else {
  180. nt.stopInternal()
  181. }
  182. }
  183. func (nt *NickTimer) Enabled() bool {
  184. return atomic.LoadUint32(&nt.enabled) == 1
  185. }
  186. func (nt *NickTimer) Timeout() (timeout time.Duration) {
  187. nt.Lock()
  188. timeout = nt.timeout
  189. nt.Unlock()
  190. return
  191. }
  192. // Touch records a nick change and updates the timer as necessary
  193. func (nt *NickTimer) Touch() {
  194. if !nt.Enabled() {
  195. return
  196. }
  197. cfnick, skeleton := nt.client.uniqueIdentifiers()
  198. account := nt.client.Account()
  199. accountForNick, method := nt.client.server.accounts.EnforcementStatus(cfnick, skeleton)
  200. enforceTimeout := method == NickReservationWithTimeout
  201. var shouldWarn, shouldRename bool
  202. func() {
  203. nt.Lock()
  204. defer nt.Unlock()
  205. // the timer will not reset as long as the squatter is targeting the same account
  206. accountChanged := accountForNick != nt.accountForNick
  207. // change state
  208. nt.nick = cfnick
  209. nt.account = account
  210. nt.accountForNick = accountForNick
  211. delinquent := accountForNick != "" && accountForNick != account
  212. if nt.timer != nil && (!enforceTimeout || !delinquent || accountChanged) {
  213. nt.timer.Stop()
  214. nt.timer = nil
  215. }
  216. if enforceTimeout && delinquent && (accountChanged || nt.timer == nil) {
  217. nt.timer = time.AfterFunc(nt.timeout, nt.processTimeout)
  218. shouldWarn = true
  219. } else if method == NickReservationStrict && delinquent {
  220. shouldRename = true // this can happen if reservation was enabled by rehash
  221. }
  222. }()
  223. if shouldWarn {
  224. nt.client.Send(nil, "NickServ", "NOTICE", nt.client.Nick(), fmt.Sprintf(ircfmt.Unescape(nt.client.t(nsTimeoutNotice)), nt.Timeout()))
  225. } else if shouldRename {
  226. nt.client.Notice(nt.client.t("Nickname is reserved by a different account"))
  227. nt.client.server.RandomlyRename(nt.client)
  228. }
  229. }
  230. // Stop stops counting time and cleans up the timer
  231. func (nt *NickTimer) Stop() {
  232. nt.Lock()
  233. defer nt.Unlock()
  234. nt.stopInternal()
  235. }
  236. func (nt *NickTimer) stopInternal() {
  237. if nt.timer != nil {
  238. nt.timer.Stop()
  239. nt.timer = nil
  240. }
  241. atomic.StoreUint32(&nt.enabled, 0)
  242. }
  243. func (nt *NickTimer) processTimeout() {
  244. baseMsg := "Nick is reserved and authentication timeout expired: %v"
  245. nt.client.Notice(fmt.Sprintf(nt.client.t(baseMsg), nt.Timeout()))
  246. nt.client.server.RandomlyRename(nt.client)
  247. }