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.

kline.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/goshuirc/irc-go/ircmatch"
  11. "github.com/tidwall/buntdb"
  12. )
  13. const (
  14. keyKlineEntry = "bans.klinev2 %s"
  15. )
  16. // KLineInfo contains the address itself and expiration time for a given network.
  17. type KLineInfo struct {
  18. // Mask that is blocked.
  19. Mask string
  20. // Matcher, to facilitate fast matching.
  21. Matcher ircmatch.Matcher
  22. // Info contains information on the ban.
  23. Info IPBanInfo
  24. }
  25. // KLineManager manages and klines.
  26. type KLineManager struct {
  27. sync.RWMutex // tier 1
  28. persistenceMutex sync.Mutex // tier 2
  29. // kline'd entries
  30. entries map[string]KLineInfo
  31. expirationTimers map[string]*time.Timer
  32. server *Server
  33. }
  34. // NewKLineManager returns a new KLineManager.
  35. func NewKLineManager(s *Server) *KLineManager {
  36. var km KLineManager
  37. km.entries = make(map[string]KLineInfo)
  38. km.expirationTimers = make(map[string]*time.Timer)
  39. km.server = s
  40. km.loadFromDatastore()
  41. return &km
  42. }
  43. // AllBans returns all bans (for use with APIs, etc).
  44. func (km *KLineManager) AllBans() map[string]IPBanInfo {
  45. allb := make(map[string]IPBanInfo)
  46. km.RLock()
  47. defer km.RUnlock()
  48. for name, info := range km.entries {
  49. allb[name] = info.Info
  50. }
  51. return allb
  52. }
  53. // AddMask adds to the blocked list.
  54. func (km *KLineManager) AddMask(mask string, duration time.Duration, reason, operReason, operName string) error {
  55. km.persistenceMutex.Lock()
  56. defer km.persistenceMutex.Unlock()
  57. info := IPBanInfo{
  58. Reason: reason,
  59. OperReason: operReason,
  60. OperName: operName,
  61. TimeCreated: time.Now().UTC(),
  62. Duration: duration,
  63. }
  64. km.addMaskInternal(mask, info)
  65. return km.persistKLine(mask, info)
  66. }
  67. func (km *KLineManager) addMaskInternal(mask string, info IPBanInfo) {
  68. kln := KLineInfo{
  69. Mask: mask,
  70. Matcher: ircmatch.MakeMatch(mask),
  71. Info: info,
  72. }
  73. var timeLeft time.Duration
  74. if info.Duration > 0 {
  75. timeLeft = info.timeLeft()
  76. if timeLeft <= 0 {
  77. return
  78. }
  79. }
  80. km.Lock()
  81. defer km.Unlock()
  82. km.entries[mask] = kln
  83. km.cancelTimer(mask)
  84. if info.Duration == 0 {
  85. return
  86. }
  87. // set up new expiration timer
  88. timeCreated := info.TimeCreated
  89. processExpiration := func() {
  90. km.Lock()
  91. defer km.Unlock()
  92. maskBan, ok := km.entries[mask]
  93. if ok && maskBan.Info.TimeCreated.Equal(timeCreated) {
  94. delete(km.entries, mask)
  95. delete(km.expirationTimers, mask)
  96. }
  97. }
  98. km.expirationTimers[mask] = time.AfterFunc(timeLeft, processExpiration)
  99. }
  100. func (km *KLineManager) cancelTimer(id string) {
  101. oldTimer := km.expirationTimers[id]
  102. if oldTimer != nil {
  103. oldTimer.Stop()
  104. delete(km.expirationTimers, id)
  105. }
  106. }
  107. func (km *KLineManager) persistKLine(mask string, info IPBanInfo) error {
  108. // save in datastore
  109. klineKey := fmt.Sprintf(keyKlineEntry, mask)
  110. // assemble json from ban info
  111. b, err := json.Marshal(info)
  112. if err != nil {
  113. return err
  114. }
  115. bstr := string(b)
  116. var setOptions *buntdb.SetOptions
  117. if info.Duration != 0 {
  118. setOptions = &buntdb.SetOptions{Expires: true, TTL: info.Duration}
  119. }
  120. err = km.server.store.Update(func(tx *buntdb.Tx) error {
  121. _, _, err := tx.Set(klineKey, bstr, setOptions)
  122. return err
  123. })
  124. return err
  125. }
  126. func (km *KLineManager) unpersistKLine(mask string) error {
  127. // save in datastore
  128. klineKey := fmt.Sprintf(keyKlineEntry, mask)
  129. return km.server.store.Update(func(tx *buntdb.Tx) error {
  130. _, err := tx.Delete(klineKey)
  131. return err
  132. })
  133. }
  134. // RemoveMask removes a mask from the blocked list.
  135. func (km *KLineManager) RemoveMask(mask string) error {
  136. km.persistenceMutex.Lock()
  137. defer km.persistenceMutex.Unlock()
  138. present := func() bool {
  139. km.Lock()
  140. defer km.Unlock()
  141. _, ok := km.entries[mask]
  142. if ok {
  143. delete(km.entries, mask)
  144. }
  145. km.cancelTimer(mask)
  146. return ok
  147. }()
  148. if !present {
  149. return errNoExistingBan
  150. }
  151. return km.unpersistKLine(mask)
  152. }
  153. // CheckMasks returns whether or not the hostmask(s) are banned, and how long they are banned for.
  154. func (km *KLineManager) CheckMasks(masks ...string) (isBanned bool, info IPBanInfo) {
  155. km.RLock()
  156. defer km.RUnlock()
  157. for _, entryInfo := range km.entries {
  158. for _, mask := range masks {
  159. if entryInfo.Matcher.Match(mask) {
  160. return true, entryInfo.Info
  161. }
  162. }
  163. }
  164. // no matches!
  165. isBanned = false
  166. return
  167. }
  168. func (km *KLineManager) loadFromDatastore() {
  169. // load from datastore
  170. klinePrefix := fmt.Sprintf(keyKlineEntry, "")
  171. km.server.store.View(func(tx *buntdb.Tx) error {
  172. tx.AscendGreaterOrEqual("", klinePrefix, func(key, value string) bool {
  173. if !strings.HasPrefix(key, klinePrefix) {
  174. return false
  175. }
  176. // get address name
  177. mask := strings.TrimPrefix(key, klinePrefix)
  178. // load ban info
  179. var info IPBanInfo
  180. err := json.Unmarshal([]byte(value), &info)
  181. if err != nil {
  182. km.server.logger.Error("internal", "couldn't unmarshal kline", err.Error())
  183. return true
  184. }
  185. // add oper name if it doesn't exist already
  186. if info.OperName == "" {
  187. info.OperName = km.server.name
  188. }
  189. // add to the server
  190. km.addMaskInternal(mask, info)
  191. return true
  192. })
  193. return nil
  194. })
  195. }
  196. func (s *Server) loadKLines() {
  197. s.klines = NewKLineManager(s)
  198. }