session.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. package state
  2. import (
  3. "net/netip"
  4. "sync"
  5. "time"
  6. "github.com/mk6i/retro-aim-server/wire"
  7. )
  8. // SessSendStatus is the result of sending a message to a user.
  9. type SessSendStatus int
  10. // RateClassState tracks the rate limiting state for a specific rate class
  11. // within a user's session.
  12. //
  13. // It embeds the static wire.RateClass configuration and maintains dynamic,
  14. // per-session state used to evaluate rate limits in real time.
  15. type RateClassState struct {
  16. // static rate limit configuration for this class
  17. wire.RateClass
  18. // CurrentLevel is the current exponential moving average for this rate
  19. // class.
  20. CurrentLevel int32
  21. // LastTime represents the last time a SNAC message was sent for this rate
  22. // class.
  23. LastTime time.Time
  24. // CurrentStatus is the last recorded rate limit status for this rate class.
  25. CurrentStatus wire.RateLimitStatus
  26. // Subscribed indicates whether the user wants to receive rate limit
  27. // parameter updates for this rate class.
  28. Subscribed bool
  29. // LimitedNow indicates whether the user is currently rate limited for this
  30. // rate class; the user is blocked from sending SNACs in this rate class
  31. // until the clear threshold is met.
  32. LimitedNow bool
  33. }
  34. const (
  35. // SessSendOK indicates message was sent to recipient
  36. SessSendOK SessSendStatus = iota
  37. // SessSendClosed indicates send did not complete because session is closed
  38. SessSendClosed
  39. // SessQueueFull indicates send failed due to full queue -- client is likely
  40. // dead
  41. SessQueueFull
  42. )
  43. // Session represents a user's current session. Unless stated otherwise, all
  44. // methods may be safely accessed by multiple goroutines.
  45. type Session struct {
  46. awayMessage string
  47. caps [][16]byte
  48. chatRoomCookie string
  49. closed bool
  50. displayScreenName DisplayScreenName
  51. identScreenName IdentScreenName
  52. idle bool
  53. idleTime time.Time
  54. msgCh chan wire.SNACMessage
  55. mutex sync.RWMutex
  56. nowFn func() time.Time
  57. signonComplete bool
  58. signonTime time.Time
  59. stopCh chan struct{}
  60. uin uint32
  61. warning uint16
  62. userInfoBitmask uint16
  63. userStatusBitmask uint32
  64. clientID string
  65. remoteAddr *netip.AddrPort
  66. lastObservedStates [5]RateClassState
  67. rateByClassID [5]RateClassState
  68. foodGroupVersions [wire.MDir + 1]uint16
  69. typingEventsEnabled bool
  70. multiConnFlag wire.MultiConnFlag
  71. }
  72. // NewSession returns a new instance of Session. By default, the user may have
  73. // up to 1000 pending messages before blocking.
  74. func NewSession() *Session {
  75. now := time.Now()
  76. return &Session{
  77. msgCh: make(chan wire.SNACMessage, 1000),
  78. nowFn: time.Now,
  79. stopCh: make(chan struct{}),
  80. signonTime: now,
  81. caps: make([][16]byte, 0),
  82. userInfoBitmask: wire.OServiceUserFlagOSCARFree,
  83. userStatusBitmask: wire.OServiceUserStatusAvailable,
  84. foodGroupVersions: func() [wire.MDir + 1]uint16 {
  85. // initialize default food groups versions to 1.0
  86. vals := [wire.MDir + 1]uint16{}
  87. vals[wire.OService] = 1
  88. vals[wire.Locate] = 1
  89. vals[wire.Buddy] = 1
  90. vals[wire.ICBM] = 1
  91. vals[wire.Advert] = 1
  92. vals[wire.Invite] = 1
  93. vals[wire.Admin] = 1
  94. vals[wire.Popup] = 1
  95. vals[wire.PermitDeny] = 1
  96. vals[wire.UserLookup] = 1
  97. vals[wire.Stats] = 1
  98. vals[wire.Translate] = 1
  99. vals[wire.ChatNav] = 1
  100. vals[wire.Chat] = 1
  101. vals[wire.ODir] = 1
  102. vals[wire.BART] = 1
  103. vals[wire.Feedbag] = 1
  104. vals[wire.ICQ] = 1
  105. vals[wire.BUCP] = 1
  106. vals[wire.Alert] = 1
  107. vals[wire.Plugin] = 1
  108. vals[wire.UnnamedFG24] = 1
  109. vals[wire.MDir] = 1
  110. return vals
  111. }(),
  112. }
  113. }
  114. func (s *Session) SetRateClasses(now time.Time, classes wire.RateLimitClasses) {
  115. s.mutex.Lock()
  116. defer s.mutex.Unlock()
  117. var newStates [5]RateClassState
  118. for i, class := range classes.All() {
  119. newStates[i] = RateClassState{
  120. CurrentLevel: class.MaxLevel,
  121. CurrentStatus: wire.RateLimitStatusClear,
  122. LastTime: now,
  123. RateClass: class,
  124. Subscribed: s.lastObservedStates[i].Subscribed,
  125. }
  126. }
  127. if s.lastObservedStates[0].ID == 0 {
  128. s.lastObservedStates = newStates
  129. } else {
  130. s.lastObservedStates = s.rateByClassID
  131. }
  132. s.rateByClassID = newStates
  133. }
  134. // SetRemoteAddr sets the user's remote IP address
  135. func (s *Session) SetRemoteAddr(remoteAddr *netip.AddrPort) {
  136. s.mutex.Lock()
  137. defer s.mutex.Unlock()
  138. s.remoteAddr = remoteAddr
  139. }
  140. // RemoteAddrs returns user's remote IP address
  141. func (s *Session) RemoteAddr() (remoteAddr *netip.AddrPort) {
  142. s.mutex.RLock()
  143. defer s.mutex.RUnlock()
  144. return s.remoteAddr
  145. }
  146. // SetUserInfoFlag sets a flag to and returns UserInfoBitmask
  147. func (s *Session) SetUserInfoFlag(flag uint16) (flags uint16) {
  148. s.mutex.Lock()
  149. defer s.mutex.Unlock()
  150. s.userInfoBitmask |= flag
  151. return s.userInfoBitmask
  152. }
  153. // ClearUserInfoFlag clear a flag from and returns UserInfoBitmask
  154. func (s *Session) ClearUserInfoFlag(flag uint16) (flags uint16) {
  155. s.mutex.Lock()
  156. defer s.mutex.Unlock()
  157. s.userInfoBitmask &^= flag
  158. return s.userInfoBitmask
  159. }
  160. // UserInfoBitmask returns UserInfoBitmask
  161. func (s *Session) UserInfoBitmask() (flags uint16) {
  162. s.mutex.RLock()
  163. defer s.mutex.RUnlock()
  164. return s.userInfoBitmask
  165. }
  166. // SetUserStatusBitmask sets the user status bitmask from the client.
  167. func (s *Session) SetUserStatusBitmask(bitmask uint32) {
  168. s.mutex.Lock()
  169. defer s.mutex.Unlock()
  170. s.userStatusBitmask = bitmask
  171. }
  172. // IncrementWarning increments the user's warning level. To decrease, pass a
  173. // negative increment value.
  174. func (s *Session) IncrementWarning(incr uint16) {
  175. s.mutex.Lock()
  176. defer s.mutex.Unlock()
  177. s.warning += incr
  178. }
  179. // Invisible returns true if the user is idle.
  180. func (s *Session) Invisible() bool {
  181. s.mutex.RLock()
  182. defer s.mutex.RUnlock()
  183. return s.userStatusBitmask&wire.OServiceUserStatusInvisible == wire.OServiceUserStatusInvisible
  184. }
  185. // SetIdentScreenName sets the user's screen name.
  186. func (s *Session) SetIdentScreenName(screenName IdentScreenName) {
  187. s.mutex.Lock()
  188. defer s.mutex.Unlock()
  189. s.identScreenName = screenName
  190. }
  191. // IdentScreenName returns the user's screen name.
  192. func (s *Session) IdentScreenName() IdentScreenName {
  193. s.mutex.RLock()
  194. defer s.mutex.RUnlock()
  195. return s.identScreenName
  196. }
  197. // SetDisplayScreenName sets the user's screen name.
  198. func (s *Session) SetDisplayScreenName(displayScreenName DisplayScreenName) {
  199. s.mutex.Lock()
  200. defer s.mutex.Unlock()
  201. s.displayScreenName = displayScreenName
  202. }
  203. // DisplayScreenName returns the user's screen name.
  204. func (s *Session) DisplayScreenName() DisplayScreenName {
  205. s.mutex.RLock()
  206. defer s.mutex.RUnlock()
  207. return s.displayScreenName
  208. }
  209. // SetSignonTime sets the user's sign-ontime.
  210. func (s *Session) SetSignonTime(t time.Time) {
  211. s.mutex.Lock()
  212. defer s.mutex.Unlock()
  213. s.signonTime = t
  214. }
  215. // SignonTime reports when the user signed on
  216. func (s *Session) SignonTime() time.Time {
  217. s.mutex.RLock()
  218. defer s.mutex.RUnlock()
  219. return s.signonTime
  220. }
  221. // Idle reports the user's idle state.
  222. func (s *Session) Idle() bool {
  223. s.mutex.RLock()
  224. defer s.mutex.RUnlock()
  225. return s.idle
  226. }
  227. // IdleTime reports when the user went idle
  228. func (s *Session) IdleTime() time.Time {
  229. s.mutex.RLock()
  230. defer s.mutex.RUnlock()
  231. return s.idleTime
  232. }
  233. // SetIdle sets the user's idle state.
  234. func (s *Session) SetIdle(dur time.Duration) {
  235. s.mutex.Lock()
  236. defer s.mutex.Unlock()
  237. s.idle = true
  238. // set the time the user became idle
  239. s.idleTime = s.nowFn().Add(-dur)
  240. }
  241. // UnsetIdle removes the user's idle state.
  242. func (s *Session) UnsetIdle() {
  243. s.mutex.Lock()
  244. defer s.mutex.Unlock()
  245. s.idle = false
  246. }
  247. // SetAwayMessage sets the user's away message.
  248. func (s *Session) SetAwayMessage(awayMessage string) {
  249. s.mutex.Lock()
  250. defer s.mutex.Unlock()
  251. s.awayMessage = awayMessage
  252. }
  253. // AwayMessage returns the user's away message.
  254. func (s *Session) AwayMessage() string {
  255. s.mutex.RLock()
  256. defer s.mutex.RUnlock()
  257. return s.awayMessage
  258. }
  259. // SetChatRoomCookie sets the chatRoomCookie for the chat room the user is currently in.
  260. func (s *Session) SetChatRoomCookie(cookie string) {
  261. s.mutex.Lock()
  262. defer s.mutex.Unlock()
  263. s.chatRoomCookie = cookie
  264. }
  265. // ChatRoomCookie gets the chatRoomCookie for the chat room the user is currently in.
  266. func (s *Session) ChatRoomCookie() string {
  267. s.mutex.RLock()
  268. defer s.mutex.RUnlock()
  269. return s.chatRoomCookie
  270. }
  271. // SignonComplete indicates whether the client has completed the sign-on sequence.
  272. func (s *Session) SignonComplete() bool {
  273. s.mutex.RLock()
  274. defer s.mutex.RUnlock()
  275. return s.signonComplete
  276. }
  277. // SetSignonComplete indicates that the client has completed the sign-on sequence.
  278. func (s *Session) SetSignonComplete() {
  279. s.mutex.Lock()
  280. defer s.mutex.Unlock()
  281. s.signonComplete = true
  282. }
  283. // UIN returns the user's ICQ number.
  284. func (s *Session) UIN() uint32 {
  285. s.mutex.RLock()
  286. defer s.mutex.RUnlock()
  287. return s.uin
  288. }
  289. // SetUIN sets the user's ICQ number.
  290. func (s *Session) SetUIN(uin uint32) {
  291. s.mutex.Lock()
  292. defer s.mutex.Unlock()
  293. s.uin = uin
  294. }
  295. // TLVUserInfo returns a TLV list containing session information required by
  296. // multiple SNAC message types that convey user information.
  297. func (s *Session) TLVUserInfo() wire.TLVUserInfo {
  298. s.mutex.RLock()
  299. defer s.mutex.RUnlock()
  300. return wire.TLVUserInfo{
  301. ScreenName: string(s.displayScreenName),
  302. WarningLevel: s.warning,
  303. TLVBlock: wire.TLVBlock{
  304. TLVList: s.userInfo(),
  305. },
  306. }
  307. }
  308. func (s *Session) userInfo() wire.TLVList {
  309. tlvs := wire.TLVList{}
  310. // sign-in timestamp
  311. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(s.signonTime.Unix())))
  312. // user info flags
  313. uFlags := s.userInfoBitmask
  314. if s.awayMessage != "" {
  315. uFlags |= wire.OServiceUserFlagUnavailable
  316. }
  317. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoUserFlags, uFlags))
  318. // user status flags
  319. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoStatus, s.userStatusBitmask))
  320. // idle status
  321. if s.idle {
  322. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoIdleTime, uint16(s.nowFn().Sub(s.idleTime).Minutes())))
  323. }
  324. // ICQ direct-connect info. The TLV is required for buddy arrival events to
  325. // work in ICQ, even if the values are set to default.
  326. if s.userInfoBitmask&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
  327. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
  328. }
  329. // capabilities (buddy icon, chat, etc...)
  330. if len(s.caps) > 0 {
  331. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoOscarCaps, s.caps))
  332. }
  333. tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)))
  334. return tlvs
  335. }
  336. // SetCaps sets capability UUIDs that represent the features the client
  337. // supports. If set, capability metadata appears in the user info TLV list.
  338. func (s *Session) SetCaps(caps [][16]byte) {
  339. s.mutex.Lock()
  340. defer s.mutex.Unlock()
  341. s.caps = caps
  342. }
  343. // Caps retrieves user capabilities.
  344. func (s *Session) Caps() [][16]byte {
  345. s.mutex.RLock()
  346. defer s.mutex.RUnlock()
  347. return s.caps
  348. }
  349. func (s *Session) Warning() uint16 {
  350. s.mutex.RLock()
  351. defer s.mutex.RUnlock()
  352. var w uint16
  353. w = s.warning
  354. return w
  355. }
  356. // ReceiveMessage returns a channel of messages relayed via this session. It
  357. // may only be read by one consumer. The channel never closes; call this method
  358. // in a select block along with Closed in order to detect session closure.
  359. func (s *Session) ReceiveMessage() chan wire.SNACMessage {
  360. return s.msgCh
  361. }
  362. // RelayMessage receives a SNAC message from a user and passes it on
  363. // asynchronously to the consumer of this session's messages. It returns
  364. // SessSendStatus to indicate whether the message was successfully sent or
  365. // not. This method is non-blocking.
  366. func (s *Session) RelayMessage(msg wire.SNACMessage) SessSendStatus {
  367. s.mutex.RLock()
  368. defer s.mutex.RUnlock()
  369. if s.closed {
  370. return SessSendClosed
  371. }
  372. select {
  373. case s.msgCh <- msg:
  374. return SessSendOK
  375. case <-s.stopCh:
  376. return SessSendClosed
  377. default:
  378. return SessQueueFull
  379. }
  380. }
  381. // Close shuts down the session's ability to relay messages. Once invoked,
  382. // RelayMessage returns SessQueueFull and Closed returns a closed channel.
  383. // It is not possible to re-open message relaying once closed. It is safe to
  384. // call from multiple go routines.
  385. func (s *Session) Close() {
  386. s.mutex.Lock()
  387. defer s.mutex.Unlock()
  388. s.close()
  389. }
  390. func (s *Session) close() {
  391. if s.closed {
  392. return
  393. }
  394. close(s.stopCh)
  395. s.closed = true
  396. }
  397. // Closed blocks until the session is closed.
  398. func (s *Session) Closed() <-chan struct{} {
  399. return s.stopCh
  400. }
  401. // SetClientID sets the client ID.
  402. func (s *Session) SetClientID(clientID string) {
  403. s.mutex.Lock()
  404. defer s.mutex.Unlock()
  405. s.clientID = clientID
  406. }
  407. // ClientID retrieves the client ID.
  408. func (s *Session) ClientID() string {
  409. s.mutex.RLock()
  410. defer s.mutex.RUnlock()
  411. return s.clientID
  412. }
  413. // SubscribeRateLimits subscribes the Session to updates for the specified
  414. // rate limit classes. Future calls to ObserveRateChanges will report changes
  415. // for these classes.
  416. func (s *Session) SubscribeRateLimits(classes []wire.RateLimitClassID) {
  417. s.mutex.Lock()
  418. defer s.mutex.Unlock()
  419. for _, classID := range classes {
  420. s.rateByClassID[classID-1].Subscribed = true
  421. }
  422. }
  423. // ObserveRateChanges updates rate limit states for all known classes and returns
  424. // any classes and class states that have changed since the previous observation.
  425. func (s *Session) ObserveRateChanges(now time.Time) (classDelta []RateClassState, stateDelta []RateClassState) {
  426. s.mutex.Lock()
  427. defer s.mutex.Unlock()
  428. for i, params := range s.rateByClassID {
  429. if !params.Subscribed {
  430. continue
  431. }
  432. state, level := wire.CheckRateLimit(params.LastTime, now, params.RateClass, params.CurrentLevel, params.LimitedNow)
  433. s.rateByClassID[i].CurrentStatus = state
  434. // clear limited now flag if passing from limited state to clear state
  435. if s.rateByClassID[i].LimitedNow && state == wire.RateLimitStatusClear {
  436. s.rateByClassID[i].LimitedNow = false
  437. s.rateByClassID[i].CurrentLevel = level
  438. }
  439. // did rate class change?
  440. if params.RateClass != s.lastObservedStates[i].RateClass {
  441. classDelta = append(classDelta, s.rateByClassID[i])
  442. }
  443. // did rate limit status change?
  444. if s.lastObservedStates[i].CurrentStatus != s.rateByClassID[i].CurrentStatus {
  445. stateDelta = append(stateDelta, s.rateByClassID[i])
  446. }
  447. // save it for next time
  448. s.lastObservedStates[i] = s.rateByClassID[i]
  449. }
  450. return classDelta, stateDelta
  451. }
  452. // EvaluateRateLimit checks and updates the session’s rate limit state
  453. // for the given rate class ID. If the rate status reaches 'disconnect',
  454. // the session is closed. Rate limits are not enforced if the user is a bot
  455. // (has wire.OServiceUserFlagBot set in their user info bitmask).
  456. func (s *Session) EvaluateRateLimit(now time.Time, rateClassID wire.RateLimitClassID) wire.RateLimitStatus {
  457. s.mutex.Lock()
  458. defer s.mutex.Unlock()
  459. if s.userInfoBitmask&wire.OServiceUserFlagBot == wire.OServiceUserFlagBot {
  460. return wire.RateLimitStatusClear // don't rate limit bots
  461. }
  462. rateClass := &s.rateByClassID[rateClassID-1]
  463. status, newLevel := wire.CheckRateLimit(rateClass.LastTime, now, rateClass.RateClass, rateClass.CurrentLevel, rateClass.LimitedNow)
  464. rateClass.CurrentLevel = newLevel
  465. rateClass.CurrentStatus = status
  466. rateClass.LastTime = now
  467. rateClass.LimitedNow = status == wire.RateLimitStatusLimited
  468. if status == wire.RateLimitStatusDisconnect {
  469. s.close()
  470. }
  471. return status
  472. }
  473. // SetFoodGroupVersions sets the client's supported food group versions
  474. func (s *Session) SetFoodGroupVersions(versions [wire.MDir + 1]uint16) {
  475. s.mutex.Lock()
  476. defer s.mutex.Unlock()
  477. s.foodGroupVersions = versions
  478. }
  479. // FoodGroupVersions retrieves the client's supported food group versions.
  480. func (s *Session) FoodGroupVersions() [wire.MDir + 1]uint16 {
  481. s.mutex.RLock()
  482. defer s.mutex.RUnlock()
  483. return s.foodGroupVersions
  484. }
  485. // TypingEventsEnabled indicates whether the client wants to send and receive
  486. // typing events.
  487. func (s *Session) TypingEventsEnabled() bool {
  488. s.mutex.RLock()
  489. defer s.mutex.RUnlock()
  490. return s.typingEventsEnabled
  491. }
  492. // SetTypingEventsEnabled sets whether the client wants to send and receive
  493. // typing events.
  494. func (s *Session) SetTypingEventsEnabled(enabled bool) {
  495. s.mutex.Lock()
  496. defer s.mutex.Unlock()
  497. s.typingEventsEnabled = enabled
  498. }
  499. // SetMultiConnFlag sets the multi-connection flag for this session.
  500. func (s *Session) SetMultiConnFlag(flag wire.MultiConnFlag) {
  501. s.mutex.Lock()
  502. defer s.mutex.Unlock()
  503. s.multiConnFlag = flag
  504. }
  505. // MultiConnFlag retrieves the multi-connection flag for this session.
  506. func (s *Session) MultiConnFlag() wire.MultiConnFlag {
  507. s.mutex.RLock()
  508. defer s.mutex.RUnlock()
  509. return s.multiConnFlag
  510. }