You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

idletimer.go 6.9KB

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