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.

connection_limits.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. )
  9. var (
  10. errTooManyClients = errors.New("Too many clients in subnet")
  11. )
  12. // ConnectionLimits manages the automated client connection limits.
  13. type ConnectionLimits struct {
  14. ipv4Mask net.IPMask
  15. ipv6Mask net.IPMask
  16. // subnetLimit is the maximum number of clients per subnet
  17. subnetLimit int
  18. // population holds IP -> count of clients connected from there
  19. population map[string]int
  20. // exemptedIPs holds IPs that are exempt from limits
  21. exemptedIPs map[string]bool
  22. // exemptedNets holds networks that are exempt from limits
  23. exemptedNets []net.IPNet
  24. }
  25. // maskAddr masks the given IPv4/6 address with our cidr limit masks.
  26. func (cl *ConnectionLimits) maskAddr(addr net.IP) net.IP {
  27. if addr.To4() == nil {
  28. // IPv6 addr
  29. addr = addr.Mask(cl.ipv6Mask)
  30. } else {
  31. // IPv4 addr
  32. addr = addr.Mask(cl.ipv4Mask)
  33. }
  34. return addr
  35. }
  36. // AddClient adds a client to our population if possible. If we can't, throws an error instead.
  37. // 'force' is used to add already-existing clients (i.e. ones that are already on the network).
  38. func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
  39. // check exempted lists
  40. // we don't track populations for exempted addresses or nets - this is by design
  41. if cl.exemptedIPs[addr.String()] {
  42. return nil
  43. }
  44. for _, ex := range cl.exemptedNets {
  45. if ex.Contains(addr) {
  46. return nil
  47. }
  48. }
  49. // check population
  50. cl.maskAddr(addr)
  51. addrString := addr.String()
  52. if cl.population[addrString]+1 > cl.subnetLimit && !force {
  53. return errTooManyClients
  54. }
  55. cl.population[addrString] = cl.population[addrString] + 1
  56. return nil
  57. }
  58. // RemoveClient removes the given address from our population
  59. func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
  60. addrString := addr.String()
  61. cl.population[addrString] = cl.population[addrString] - 1
  62. // safety limiter
  63. if cl.population[addrString] < 0 {
  64. cl.population[addrString] = 0
  65. }
  66. }
  67. // NewConnectionLimits returns a new connection limit handler.
  68. func NewConnectionLimits(config ConnectionLimitsConfig) (*ConnectionLimits, error) {
  69. var cl ConnectionLimits
  70. cl.population = make(map[string]int)
  71. cl.exemptedIPs = make(map[string]bool)
  72. cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
  73. cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
  74. // subnetLimit is explicitly NOT capped at a minimum of one.
  75. // this is so that CL config can be used to allow ONLY clients from exempted IPs/nets
  76. cl.subnetLimit = config.IPsPerCidr
  77. // assemble exempted nets
  78. for _, cidr := range config.Exempted {
  79. ipaddr := net.ParseIP(cidr)
  80. _, netaddr, err := net.ParseCIDR(cidr)
  81. if ipaddr == nil && err != nil {
  82. return nil, fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
  83. }
  84. if ipaddr != nil {
  85. cl.exemptedIPs[ipaddr.String()] = true
  86. } else {
  87. cl.exemptedNets = append(cl.exemptedNets, *netaddr)
  88. }
  89. }
  90. return &cl, nil
  91. }