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

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