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 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. "sync"
  7. "github.com/goshuirc/irc-go/ircmatch"
  8. "github.com/tidwall/buntdb"
  9. )
  10. const (
  11. keyKlineEntry = "bans.kline %s"
  12. )
  13. // KLineInfo contains the address itself and expiration time for a given network.
  14. type KLineInfo struct {
  15. // Mask that is blocked.
  16. Mask string
  17. // Matcher, to facilitate fast matching.
  18. Matcher ircmatch.Matcher
  19. // Info contains information on the ban.
  20. Info IPBanInfo
  21. }
  22. // KLineManager manages and klines.
  23. type KLineManager struct {
  24. sync.RWMutex // tier 1
  25. // kline'd entries
  26. entries map[string]*KLineInfo
  27. }
  28. // NewKLineManager returns a new KLineManager.
  29. func NewKLineManager() *KLineManager {
  30. var km KLineManager
  31. km.entries = make(map[string]*KLineInfo)
  32. return &km
  33. }
  34. // AllBans returns all bans (for use with APIs, etc).
  35. func (km *KLineManager) AllBans() map[string]IPBanInfo {
  36. allb := make(map[string]IPBanInfo)
  37. km.RLock()
  38. defer km.RUnlock()
  39. for name, info := range km.entries {
  40. allb[name] = info.Info
  41. }
  42. return allb
  43. }
  44. // AddMask adds to the blocked list.
  45. func (km *KLineManager) AddMask(mask string, length *IPRestrictTime, reason, operReason, operName string) {
  46. kln := KLineInfo{
  47. Mask: mask,
  48. Matcher: ircmatch.MakeMatch(mask),
  49. Info: IPBanInfo{
  50. Time: length,
  51. Reason: reason,
  52. OperReason: operReason,
  53. OperName: operName,
  54. },
  55. }
  56. km.Lock()
  57. km.entries[mask] = &kln
  58. km.Unlock()
  59. }
  60. // RemoveMask removes a mask from the blocked list.
  61. func (km *KLineManager) RemoveMask(mask string) {
  62. km.Lock()
  63. delete(km.entries, mask)
  64. km.Unlock()
  65. }
  66. // CheckMasks returns whether or not the hostmask(s) are banned, and how long they are banned for.
  67. func (km *KLineManager) CheckMasks(masks ...string) (isBanned bool, info *IPBanInfo) {
  68. doCleanup := false
  69. defer func() {
  70. // asynchronously remove expired bans
  71. if doCleanup {
  72. go func() {
  73. km.Lock()
  74. defer km.Unlock()
  75. for key, entry := range km.entries {
  76. if entry.Info.Time.IsExpired() {
  77. delete(km.entries, key)
  78. }
  79. }
  80. }()
  81. }
  82. }()
  83. km.RLock()
  84. defer km.RUnlock()
  85. for _, entryInfo := range km.entries {
  86. if entryInfo.Info.Time != nil && entryInfo.Info.Time.IsExpired() {
  87. doCleanup = true
  88. continue
  89. }
  90. matches := false
  91. for _, mask := range masks {
  92. if entryInfo.Matcher.Match(mask) {
  93. matches = true
  94. break
  95. }
  96. }
  97. if matches {
  98. return true, &entryInfo.Info
  99. }
  100. }
  101. // no matches!
  102. return false, nil
  103. }
  104. func (s *Server) loadKLines() {
  105. s.klines = NewKLineManager()
  106. // load from datastore
  107. s.store.View(func(tx *buntdb.Tx) error {
  108. //TODO(dan): We could make this safer
  109. tx.AscendKeys("bans.kline *", func(key, value string) bool {
  110. // get address name
  111. key = key[len("bans.kline "):]
  112. mask := key
  113. // load ban info
  114. var info IPBanInfo
  115. json.Unmarshal([]byte(value), &info)
  116. // add oper name if it doesn't exist already
  117. if info.OperName == "" {
  118. info.OperName = s.name
  119. }
  120. // add to the server
  121. s.klines.AddMask(mask, info.Time, info.Reason, info.OperReason, info.OperName)
  122. return true // true to continue I guess?
  123. })
  124. return nil
  125. })
  126. }