Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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