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.

dline.go 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. "sync"
  9. "time"
  10. "encoding/json"
  11. "github.com/tidwall/buntdb"
  12. )
  13. const (
  14. keyDlineEntry = "bans.dline %s"
  15. )
  16. var (
  17. errNoExistingBan = errors.New("Ban does not exist")
  18. )
  19. // IPRestrictTime contains the expiration info about the given IP.
  20. type IPRestrictTime struct {
  21. // Duration is how long this block lasts for.
  22. Duration time.Duration `json:"duration"`
  23. // Expires is when this block expires.
  24. Expires time.Time `json:"expires"`
  25. }
  26. // IsExpired returns true if the time has expired.
  27. func (iptime *IPRestrictTime) IsExpired() bool {
  28. return iptime.Expires.Before(time.Now())
  29. }
  30. // IPBanInfo holds info about an IP/net ban.
  31. type IPBanInfo struct {
  32. // Reason is the ban reason.
  33. Reason string `json:"reason"`
  34. // OperReason is an oper ban reason.
  35. OperReason string `json:"oper_reason"`
  36. // OperName is the oper who set the ban.
  37. OperName string `json:"oper_name"`
  38. // Time holds details about the duration, if it exists.
  39. Time *IPRestrictTime `json:"time"`
  40. }
  41. // BanMessage returns the ban message.
  42. func (info IPBanInfo) BanMessage(message string) string {
  43. message = fmt.Sprintf(message, info.Reason)
  44. if info.Time != nil {
  45. message += fmt.Sprintf(" [%s]", info.Time.Duration.String())
  46. }
  47. return message
  48. }
  49. // dLineAddr contains the address itself and expiration time for a given network.
  50. type dLineAddr struct {
  51. // Address is the address that is blocked.
  52. Address net.IP
  53. // Info contains information on the ban.
  54. Info IPBanInfo
  55. }
  56. // dLineNet contains the net itself and expiration time for a given network.
  57. type dLineNet struct {
  58. // Network is the network that is blocked.
  59. Network net.IPNet
  60. // Info contains information on the ban.
  61. Info IPBanInfo
  62. }
  63. // DLineManager manages and dlines.
  64. type DLineManager struct {
  65. sync.RWMutex // tier 1
  66. // addresses that are dlined
  67. addresses map[string]*dLineAddr
  68. // networks that are dlined
  69. networks map[string]*dLineNet
  70. }
  71. // NewDLineManager returns a new DLineManager.
  72. func NewDLineManager() *DLineManager {
  73. var dm DLineManager
  74. dm.addresses = make(map[string]*dLineAddr)
  75. dm.networks = make(map[string]*dLineNet)
  76. return &dm
  77. }
  78. // AllBans returns all bans (for use with APIs, etc).
  79. func (dm *DLineManager) AllBans() map[string]IPBanInfo {
  80. allb := make(map[string]IPBanInfo)
  81. dm.RLock()
  82. defer dm.RUnlock()
  83. for name, info := range dm.addresses {
  84. allb[name] = info.Info
  85. }
  86. for name, info := range dm.networks {
  87. allb[name] = info.Info
  88. }
  89. return allb
  90. }
  91. // AddNetwork adds a network to the blocked list.
  92. func (dm *DLineManager) AddNetwork(network net.IPNet, length *IPRestrictTime, reason, operReason, operName string) {
  93. netString := network.String()
  94. dln := dLineNet{
  95. Network: network,
  96. Info: IPBanInfo{
  97. Time: length,
  98. Reason: reason,
  99. OperReason: operReason,
  100. OperName: operName,
  101. },
  102. }
  103. dm.Lock()
  104. dm.networks[netString] = &dln
  105. dm.Unlock()
  106. }
  107. // RemoveNetwork removes a network from the blocked list.
  108. func (dm *DLineManager) RemoveNetwork(network net.IPNet) {
  109. netString := network.String()
  110. dm.Lock()
  111. delete(dm.networks, netString)
  112. dm.Unlock()
  113. }
  114. // AddIP adds an IP address to the blocked list.
  115. func (dm *DLineManager) AddIP(addr net.IP, length *IPRestrictTime, reason, operReason, operName string) {
  116. addrString := addr.String()
  117. dla := dLineAddr{
  118. Address: addr,
  119. Info: IPBanInfo{
  120. Time: length,
  121. Reason: reason,
  122. OperReason: operReason,
  123. OperName: operName,
  124. },
  125. }
  126. dm.Lock()
  127. dm.addresses[addrString] = &dla
  128. dm.Unlock()
  129. }
  130. // RemoveIP removes an IP from the blocked list.
  131. func (dm *DLineManager) RemoveIP(addr net.IP) {
  132. addrString := addr.String()
  133. dm.Lock()
  134. delete(dm.addresses, addrString)
  135. dm.Unlock()
  136. }
  137. // CheckIP returns whether or not an IP address was banned, and how long it is banned for.
  138. func (dm *DLineManager) CheckIP(addr net.IP) (isBanned bool, info *IPBanInfo) {
  139. // check IP addr
  140. addrString := addr.String()
  141. dm.RLock()
  142. addrInfo := dm.addresses[addrString]
  143. dm.RUnlock()
  144. if addrInfo != nil {
  145. if addrInfo.Info.Time != nil {
  146. if addrInfo.Info.Time.IsExpired() {
  147. // ban on IP has expired, remove it from our blocked list
  148. dm.RemoveIP(addr)
  149. } else {
  150. return true, &addrInfo.Info
  151. }
  152. } else {
  153. return true, &addrInfo.Info
  154. }
  155. }
  156. // check networks
  157. doCleanup := false
  158. defer func() {
  159. if doCleanup {
  160. go func() {
  161. dm.Lock()
  162. defer dm.Unlock()
  163. for key, netInfo := range dm.networks {
  164. if netInfo.Info.Time.IsExpired() {
  165. delete(dm.networks, key)
  166. }
  167. }
  168. }()
  169. }
  170. }()
  171. dm.RLock()
  172. defer dm.RUnlock()
  173. for _, netInfo := range dm.networks {
  174. if netInfo.Info.Time != nil && netInfo.Info.Time.IsExpired() {
  175. // expired ban, ignore and clean up later
  176. doCleanup = true
  177. } else if netInfo.Network.Contains(addr) {
  178. return true, &netInfo.Info
  179. }
  180. }
  181. // no matches!
  182. return false, nil
  183. }
  184. func (s *Server) loadDLines() {
  185. s.dlines = NewDLineManager()
  186. // load from datastore
  187. s.store.View(func(tx *buntdb.Tx) error {
  188. //TODO(dan): We could make this safer
  189. tx.AscendKeys("bans.dline *", func(key, value string) bool {
  190. // get address name
  191. key = key[len("bans.dline "):]
  192. // load addr/net
  193. var hostAddr net.IP
  194. var hostNet *net.IPNet
  195. _, hostNet, err := net.ParseCIDR(key)
  196. if err != nil {
  197. hostAddr = net.ParseIP(key)
  198. }
  199. // load ban info
  200. var info IPBanInfo
  201. json.Unmarshal([]byte(value), &info)
  202. // set opername if it isn't already set
  203. if info.OperName == "" {
  204. info.OperName = s.name
  205. }
  206. // add to the server
  207. if hostNet == nil {
  208. s.dlines.AddIP(hostAddr, info.Time, info.Reason, info.OperReason, info.OperName)
  209. } else {
  210. s.dlines.AddNetwork(*hostNet, info.Time, info.Reason, info.OperReason, info.OperName)
  211. }
  212. return true // true to continue I guess?
  213. })
  214. return nil
  215. })
  216. }