Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

kline.go 9.7KB

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