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.4KB

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