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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. "time"
  9. "github.com/DanielOaks/girc-go/ircmatch"
  10. "github.com/DanielOaks/girc-go/ircmsg"
  11. "github.com/DanielOaks/oragono/irc/custime"
  12. "github.com/tidwall/buntdb"
  13. )
  14. const (
  15. keyKlineEntry = "bans.kline %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 ircmatch.Matcher
  23. // Info contains information on the ban.
  24. Info IPBanInfo
  25. }
  26. // KLineManager manages and klines.
  27. type KLineManager struct {
  28. // kline'd entries
  29. entries map[string]*KLineInfo
  30. }
  31. // NewKLineManager returns a new KLineManager.
  32. func NewKLineManager() *KLineManager {
  33. var km KLineManager
  34. km.entries = make(map[string]*KLineInfo)
  35. return &km
  36. }
  37. // AllBans returns all bans (for use with APIs, etc).
  38. func (km *KLineManager) AllBans() map[string]IPBanInfo {
  39. allb := make(map[string]IPBanInfo)
  40. for name, info := range km.entries {
  41. allb[name] = info.Info
  42. }
  43. return allb
  44. }
  45. // AddMask adds to the blocked list.
  46. func (km *KLineManager) AddMask(mask string, length *IPRestrictTime, reason string, operReason string) {
  47. kln := KLineInfo{
  48. Mask: mask,
  49. Matcher: ircmatch.MakeMatch(mask),
  50. Info: IPBanInfo{
  51. Time: length,
  52. Reason: reason,
  53. OperReason: operReason,
  54. },
  55. }
  56. km.entries[mask] = &kln
  57. }
  58. // RemoveMask removes a mask from the blocked list.
  59. func (km *KLineManager) RemoveMask(mask string) {
  60. delete(km.entries, mask)
  61. }
  62. // CheckMasks returns whether or not the hostmask(s) are banned, and how long they are banned for.
  63. func (km *KLineManager) CheckMasks(masks ...string) (isBanned bool, info *IPBanInfo) {
  64. // check networks
  65. var masksToRemove []string
  66. for _, entryInfo := range km.entries {
  67. var matches bool
  68. for _, mask := range masks {
  69. if entryInfo.Matcher.Match(mask) {
  70. matches = true
  71. break
  72. }
  73. }
  74. if !matches {
  75. continue
  76. }
  77. if entryInfo.Info.Time != nil {
  78. if entryInfo.Info.Time.IsExpired() {
  79. // ban on network has expired, remove it from our blocked list
  80. masksToRemove = append(masksToRemove, entryInfo.Mask)
  81. } else {
  82. return true, &entryInfo.Info
  83. }
  84. } else {
  85. return true, &entryInfo.Info
  86. }
  87. }
  88. // remove expired networks
  89. for _, expiredMask := range masksToRemove {
  90. km.RemoveMask(expiredMask)
  91. }
  92. // no matches!
  93. return false, nil
  94. }
  95. // KLINE [MYSELF] [duration] <mask> [ON <server>] [reason [| oper reason]]
  96. func klineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  97. // check oper permissions
  98. if !client.class.Capabilities["oper:local_ban"] {
  99. client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
  100. return false
  101. }
  102. currentArg := 0
  103. // when setting a ban that covers the oper's current connection, we require them to say
  104. // "KLINE MYSELF" so that we're sure they really mean it.
  105. var klineMyself bool
  106. if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "myself" {
  107. klineMyself = true
  108. currentArg++
  109. }
  110. // duration
  111. duration, err := custime.ParseDuration(msg.Params[currentArg])
  112. durationIsUsed := err == nil
  113. if durationIsUsed {
  114. currentArg++
  115. }
  116. // get mask
  117. if len(msg.Params) < currentArg+1 {
  118. client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters")
  119. return false
  120. }
  121. mask := strings.ToLower(msg.Params[currentArg])
  122. currentArg++
  123. // check mask
  124. if !strings.Contains(mask, "!") && !strings.Contains(mask, "@") {
  125. mask = mask + "!*@*"
  126. } else if !strings.Contains(mask, "@") {
  127. mask = mask + "@*"
  128. }
  129. matcher := ircmatch.MakeMatch(mask)
  130. for _, clientMask := range client.AllNickmasks() {
  131. if !klineMyself && matcher.Match(clientMask) {
  132. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "This ban matches you. To KLINE yourself, you must use the command: /KLINE MYSELF <arguments>")
  133. return false
  134. }
  135. }
  136. // check remote
  137. if len(msg.Params) > currentArg && msg.Params[currentArg] == "ON" {
  138. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Remote servers not yet supported")
  139. return false
  140. }
  141. // get comment(s)
  142. reason := "No reason given"
  143. operReason := "No reason given"
  144. if len(msg.Params) > currentArg {
  145. tempReason := strings.TrimSpace(msg.Params[currentArg])
  146. if len(tempReason) > 0 && tempReason != "|" {
  147. tempReasons := strings.SplitN(tempReason, "|", 2)
  148. if tempReasons[0] != "" {
  149. reason = tempReasons[0]
  150. }
  151. if len(tempReasons) > 1 && tempReasons[1] != "" {
  152. operReason = tempReasons[1]
  153. } else {
  154. operReason = reason
  155. }
  156. }
  157. }
  158. // assemble ban info
  159. var banTime *IPRestrictTime
  160. if durationIsUsed {
  161. banTime = &IPRestrictTime{
  162. Duration: duration,
  163. Expires: time.Now().Add(duration),
  164. }
  165. }
  166. info := IPBanInfo{
  167. Reason: reason,
  168. OperReason: operReason,
  169. Time: banTime,
  170. }
  171. // save in datastore
  172. err = server.store.Update(func(tx *buntdb.Tx) error {
  173. klineKey := fmt.Sprintf(keyKlineEntry, mask)
  174. // assemble json from ban info
  175. b, err := json.Marshal(info)
  176. if err != nil {
  177. return err
  178. }
  179. tx.Set(klineKey, string(b), nil)
  180. return nil
  181. })
  182. server.klines.AddMask(mask, banTime, reason, operReason)
  183. if durationIsUsed {
  184. client.Notice(fmt.Sprintf("Added temporary (%s) K-Line for %s", duration.String(), mask))
  185. } else {
  186. client.Notice(fmt.Sprintf("Added K-Line for %s", mask))
  187. }
  188. return false
  189. }
  190. func unKLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  191. // check oper permissions
  192. if !client.class.Capabilities["oper:local_unban"] {
  193. client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
  194. return false
  195. }
  196. // get host
  197. mask := msg.Params[0]
  198. if !strings.Contains(mask, "!") && !strings.Contains(mask, "@") {
  199. mask = mask + "!*@*"
  200. } else if !strings.Contains(mask, "@") {
  201. mask = mask + "@*"
  202. }
  203. // save in datastore
  204. err := server.store.Update(func(tx *buntdb.Tx) error {
  205. klineKey := fmt.Sprintf(keyKlineEntry, mask)
  206. // check if it exists or not
  207. val, err := tx.Get(klineKey)
  208. if val == "" {
  209. return errNoExistingBan
  210. } else if err != nil {
  211. return err
  212. }
  213. tx.Delete(klineKey)
  214. return nil
  215. })
  216. if err != nil {
  217. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, fmt.Sprintf("Could not remove ban [%s]", err.Error()))
  218. return false
  219. }
  220. server.klines.RemoveMask(mask)
  221. client.Notice(fmt.Sprintf("Removed K-Line for %s", mask))
  222. return false
  223. }
  224. func (s *Server) loadKLines() {
  225. s.klines = NewKLineManager()
  226. // load from datastore
  227. s.store.View(func(tx *buntdb.Tx) error {
  228. //TODO(dan): We could make this safer
  229. tx.AscendKeys("bans.kline *", func(key, value string) bool {
  230. // get address name
  231. key = key[len("bans.kline "):]
  232. mask := key
  233. // load ban info
  234. var info IPBanInfo
  235. json.Unmarshal([]byte(value), &info)
  236. // add to the server
  237. s.klines.AddMask(mask, info.Time, info.Reason, info.OperReason)
  238. return true // true to continue I guess?
  239. })
  240. return nil
  241. })
  242. }