您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

dline.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. "time"
  9. "strings"
  10. "encoding/json"
  11. "github.com/DanielOaks/girc-go/ircmsg"
  12. "github.com/DanielOaks/oragono/irc/custime"
  13. "github.com/tidwall/buntdb"
  14. )
  15. const (
  16. keyDlineEntry = "bans.dline %s"
  17. )
  18. var (
  19. errNoExistingBan = errors.New("Ban does not exist")
  20. )
  21. // IPRestrictTime contains the expiration info about the given IP.
  22. type IPRestrictTime struct {
  23. // Duration is how long this block lasts for.
  24. Duration time.Duration `json:"duration"`
  25. // Expires is when this block expires.
  26. Expires time.Time `json:"expires"`
  27. }
  28. // IsExpired returns true if the time has expired.
  29. func (iptime *IPRestrictTime) IsExpired() bool {
  30. return iptime.Expires.Before(time.Now())
  31. }
  32. // IPBanInfo holds info about an IP/net ban.
  33. type IPBanInfo struct {
  34. // Reason is the ban reason.
  35. Reason string `json:"reason"`
  36. // OperReason is an oper ban reason.
  37. OperReason string `json:"oper_reason"`
  38. // Time holds details about the duration, if it exists.
  39. Time *IPRestrictTime `json:"time"`
  40. }
  41. // dLineAddr contains the address itself and expiration time for a given network.
  42. type dLineAddr struct {
  43. // Address is the address that is blocked.
  44. Address net.IP
  45. // Info contains information on the ban.
  46. Info IPBanInfo
  47. }
  48. // dLineNet contains the net itself and expiration time for a given network.
  49. type dLineNet struct {
  50. // Network is the network that is blocked.
  51. Network net.IPNet
  52. // Info contains information on the ban.
  53. Info IPBanInfo
  54. }
  55. // DLineManager manages and dlines.
  56. type DLineManager struct {
  57. // addresses that are dlined
  58. addresses map[string]*dLineAddr
  59. // networks that are dlined
  60. networks map[string]*dLineNet
  61. }
  62. // NewDLineManager returns a new DLineManager.
  63. func NewDLineManager() *DLineManager {
  64. var dm DLineManager
  65. dm.addresses = make(map[string]*dLineAddr)
  66. dm.networks = make(map[string]*dLineNet)
  67. return &dm
  68. }
  69. // AllBans returns all bans (for use with APIs, etc).
  70. func (dm *DLineManager) AllBans() map[string]IPBanInfo {
  71. allb := make(map[string]IPBanInfo)
  72. for name, info := range dm.addresses {
  73. allb[name] = info.Info
  74. }
  75. for name, info := range dm.networks {
  76. allb[name] = info.Info
  77. }
  78. return allb
  79. }
  80. // AddNetwork adds a network to the blocked list.
  81. func (dm *DLineManager) AddNetwork(network net.IPNet, length *IPRestrictTime, reason string, operReason string) {
  82. netString := network.String()
  83. dln := dLineNet{
  84. Network: network,
  85. Info: IPBanInfo{
  86. Time: length,
  87. Reason: reason,
  88. OperReason: operReason,
  89. },
  90. }
  91. dm.networks[netString] = &dln
  92. }
  93. // RemoveNetwork removes a network from the blocked list.
  94. func (dm *DLineManager) RemoveNetwork(network net.IPNet) {
  95. netString := network.String()
  96. delete(dm.networks, netString)
  97. }
  98. // AddIP adds an IP address to the blocked list.
  99. func (dm *DLineManager) AddIP(addr net.IP, length *IPRestrictTime, reason string, operReason string) {
  100. addrString := addr.String()
  101. dla := dLineAddr{
  102. Address: addr,
  103. Info: IPBanInfo{
  104. Time: length,
  105. Reason: reason,
  106. OperReason: operReason,
  107. },
  108. }
  109. dm.addresses[addrString] = &dla
  110. }
  111. // RemoveIP removes an IP from the blocked list.
  112. func (dm *DLineManager) RemoveIP(addr net.IP) {
  113. addrString := addr.String()
  114. delete(dm.addresses, addrString)
  115. }
  116. // CheckIP returns whether or not an IP address was banned, and how long it is banned for.
  117. func (dm *DLineManager) CheckIP(addr net.IP) (isBanned bool, info *IPBanInfo) {
  118. // check IP addr
  119. addrString := addr.String()
  120. addrInfo := dm.addresses[addrString]
  121. if addrInfo != nil {
  122. if addrInfo.Info.Time != nil {
  123. if addrInfo.Info.Time.IsExpired() {
  124. // ban on IP has expired, remove it from our blocked list
  125. dm.RemoveIP(addr)
  126. } else {
  127. return true, &addrInfo.Info
  128. }
  129. } else {
  130. return true, &addrInfo.Info
  131. }
  132. }
  133. // check networks
  134. var netsToRemove []net.IPNet
  135. for _, netInfo := range dm.networks {
  136. if !netInfo.Network.Contains(addr) {
  137. continue
  138. }
  139. if netInfo.Info.Time != nil {
  140. if netInfo.Info.Time.IsExpired() {
  141. // ban on network has expired, remove it from our blocked list
  142. netsToRemove = append(netsToRemove, netInfo.Network)
  143. } else {
  144. return true, &addrInfo.Info
  145. }
  146. } else {
  147. return true, &addrInfo.Info
  148. }
  149. }
  150. // remove expired networks
  151. for _, expiredNet := range netsToRemove {
  152. dm.RemoveNetwork(expiredNet)
  153. }
  154. // no matches!
  155. return false, nil
  156. }
  157. // DLINE [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]
  158. func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  159. // check oper permissions
  160. if !client.class.Capabilities["oper:local_ban"] {
  161. client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
  162. return false
  163. }
  164. currentArg := 0
  165. // when setting a ban that covers the oper's current connection, we require them to say
  166. // "DLINE MYSELF" so that we're sure they really mean it.
  167. var dlineMyself bool
  168. if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "myself" {
  169. dlineMyself = true
  170. currentArg++
  171. }
  172. // duration
  173. duration, err := custime.ParseDuration(msg.Params[currentArg])
  174. durationIsUsed := err == nil
  175. if durationIsUsed {
  176. currentArg++
  177. }
  178. // get host
  179. if len(msg.Params) < currentArg+1 {
  180. client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, "Not enough parameters")
  181. return false
  182. }
  183. hostString := msg.Params[currentArg]
  184. currentArg++
  185. // check host
  186. var hostAddr net.IP
  187. var hostNet *net.IPNet
  188. _, hostNet, err = net.ParseCIDR(hostString)
  189. if err != nil {
  190. hostAddr = net.ParseIP(hostString)
  191. }
  192. if hostAddr == nil && hostNet == nil {
  193. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Could not parse IP address or CIDR network")
  194. return false
  195. }
  196. if hostNet == nil {
  197. hostString = hostAddr.String()
  198. if !dlineMyself && hostAddr.Equal(net.ParseIP(IPString(client.socket.conn.RemoteAddr()))) {
  199. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>")
  200. return false
  201. }
  202. } else {
  203. hostString = hostNet.String()
  204. if !dlineMyself && hostNet.Contains(net.ParseIP(IPString(client.socket.conn.RemoteAddr()))) {
  205. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "This ban matches you. To DLINE yourself, you must use the command: /DLINE MYSELF <arguments>")
  206. return false
  207. }
  208. }
  209. // check remote
  210. if len(msg.Params) > currentArg && msg.Params[currentArg] == "ON" {
  211. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Remote servers not yet supported")
  212. return false
  213. }
  214. // get comment(s)
  215. reason := "No reason given"
  216. operReason := "No reason given"
  217. if len(msg.Params) > currentArg {
  218. tempReason := strings.TrimSpace(msg.Params[currentArg])
  219. if len(tempReason) > 0 && tempReason != "|" {
  220. tempReasons := strings.SplitN(tempReason, "|", 2)
  221. if tempReasons[0] != "" {
  222. reason = tempReasons[0]
  223. }
  224. if len(tempReasons) > 1 && tempReasons[1] != "" {
  225. operReason = tempReasons[1]
  226. } else {
  227. operReason = reason
  228. }
  229. }
  230. }
  231. // assemble ban info
  232. var banTime *IPRestrictTime
  233. if durationIsUsed {
  234. banTime = &IPRestrictTime{
  235. Duration: duration,
  236. Expires: time.Now().Add(duration),
  237. }
  238. }
  239. info := IPBanInfo{
  240. Reason: reason,
  241. OperReason: operReason,
  242. Time: banTime,
  243. }
  244. // save in datastore
  245. err = server.store.Update(func(tx *buntdb.Tx) error {
  246. dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
  247. // assemble json from ban info
  248. b, err := json.Marshal(info)
  249. if err != nil {
  250. return err
  251. }
  252. tx.Set(dlineKey, string(b), nil)
  253. return nil
  254. })
  255. if hostNet == nil {
  256. server.dlines.AddIP(hostAddr, banTime, reason, operReason)
  257. } else {
  258. server.dlines.AddNetwork(*hostNet, banTime, reason, operReason)
  259. }
  260. if durationIsUsed {
  261. client.Notice(fmt.Sprintf("Added temporary (%s) D-Line for %s", duration.String(), hostString))
  262. } else {
  263. client.Notice(fmt.Sprintf("Added D-Line for %s", hostString))
  264. }
  265. return false
  266. }
  267. func unDLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  268. // check oper permissions
  269. if !client.class.Capabilities["oper:local_unban"] {
  270. client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, "Insufficient oper privs")
  271. return false
  272. }
  273. // get host
  274. hostString := msg.Params[0]
  275. // check host
  276. var hostAddr net.IP
  277. var hostNet *net.IPNet
  278. _, hostNet, err := net.ParseCIDR(hostString)
  279. if err != nil {
  280. hostAddr = net.ParseIP(hostString)
  281. }
  282. if hostAddr == nil && hostNet == nil {
  283. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, "Could not parse IP address or CIDR network")
  284. return false
  285. }
  286. if hostNet == nil {
  287. hostString = hostAddr.String()
  288. } else {
  289. hostString = hostNet.String()
  290. }
  291. // save in datastore
  292. err = server.store.Update(func(tx *buntdb.Tx) error {
  293. dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
  294. // check if it exists or not
  295. val, err := tx.Get(dlineKey)
  296. if val == "" {
  297. return errNoExistingBan
  298. } else if err != nil {
  299. return err
  300. }
  301. tx.Delete(dlineKey)
  302. return nil
  303. })
  304. if err != nil {
  305. client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, fmt.Sprintf("Could not remove ban [%s]", err.Error()))
  306. return false
  307. }
  308. if hostNet == nil {
  309. server.dlines.RemoveIP(hostAddr)
  310. } else {
  311. server.dlines.RemoveNetwork(*hostNet)
  312. }
  313. client.Notice(fmt.Sprintf("Removed D-Line for %s", hostString))
  314. return false
  315. }
  316. func (s *Server) loadDLines() {
  317. s.dlines = NewDLineManager()
  318. // load from datastore
  319. s.store.View(func(tx *buntdb.Tx) error {
  320. //TODO(dan): We could make this safer
  321. tx.AscendKeys("bans.dline *", func(key, value string) bool {
  322. // get address name
  323. key = key[len("bans.dline "):]
  324. // load addr/net
  325. var hostAddr net.IP
  326. var hostNet *net.IPNet
  327. _, hostNet, err := net.ParseCIDR(key)
  328. if err != nil {
  329. hostAddr = net.ParseIP(key)
  330. }
  331. // load ban info
  332. var info IPBanInfo
  333. json.Unmarshal([]byte(value), &info)
  334. // add to the server
  335. if hostNet == nil {
  336. s.dlines.AddIP(hostAddr, info.Time, info.Reason, info.OperReason)
  337. } else {
  338. s.dlines.AddNetwork(*hostNet, info.Time, info.Reason, info.OperReason)
  339. }
  340. return true // true to continue I guess?
  341. })
  342. return nil
  343. })
  344. }