Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

kline.go 8.8KB

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