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_throttling.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "net"
  7. "time"
  8. )
  9. // ThrottleDetails holds the connection-throttling details for a subnet/IP.
  10. type ThrottleDetails struct {
  11. Start time.Time
  12. ClientCount int
  13. }
  14. // ConnectionThrottle manages automated client connection throttling.
  15. type ConnectionThrottle struct {
  16. enabled bool
  17. ipv4Mask net.IPMask
  18. ipv6Mask net.IPMask
  19. subnetLimit int
  20. duration time.Duration
  21. population map[string]ThrottleDetails
  22. // used by the server to ban clients that go over this limit
  23. BanDuration time.Duration
  24. BanMessage string
  25. BanMessageBytes []byte
  26. // exemptedIPs holds IPs that are exempt from limits
  27. exemptedIPs map[string]bool
  28. // exemptedNets holds networks that are exempt from limits
  29. exemptedNets []net.IPNet
  30. }
  31. // maskAddr masks the given IPv4/6 address with our cidr limit masks.
  32. func (ct *ConnectionThrottle) maskAddr(addr net.IP) net.IP {
  33. if addr.To4() == nil {
  34. // IPv6 addr
  35. addr = addr.Mask(ct.ipv6Mask)
  36. } else {
  37. // IPv4 addr
  38. addr = addr.Mask(ct.ipv4Mask)
  39. }
  40. return addr
  41. }
  42. // ResetFor removes any existing count for the given address.
  43. func (ct *ConnectionThrottle) ResetFor(addr net.IP) {
  44. if !ct.enabled {
  45. return
  46. }
  47. // remove
  48. ct.maskAddr(addr)
  49. addrString := addr.String()
  50. delete(ct.population, addrString)
  51. }
  52. // AddClient introduces a new client connection if possible. If we can't, throws an error instead.
  53. func (ct *ConnectionThrottle) AddClient(addr net.IP) error {
  54. if !ct.enabled {
  55. return nil
  56. }
  57. // check exempted lists
  58. if ct.exemptedIPs[addr.String()] {
  59. return nil
  60. }
  61. for _, ex := range ct.exemptedNets {
  62. if ex.Contains(addr) {
  63. return nil
  64. }
  65. }
  66. // check throttle
  67. ct.maskAddr(addr)
  68. addrString := addr.String()
  69. details, exists := ct.population[addrString]
  70. if !exists || details.Start.Add(ct.duration).Before(time.Now()) {
  71. details = ThrottleDetails{
  72. Start: time.Now(),
  73. }
  74. }
  75. if details.ClientCount+1 > ct.subnetLimit {
  76. return errTooManyClients
  77. }
  78. details.ClientCount++
  79. ct.population[addrString] = details
  80. return nil
  81. }
  82. // NewConnectionThrottle returns a new client connection throttler.
  83. func NewConnectionThrottle(config ConnectionThrottleConfig) (*ConnectionThrottle, error) {
  84. var ct ConnectionThrottle
  85. ct.enabled = config.Enabled
  86. ct.population = make(map[string]ThrottleDetails)
  87. ct.exemptedIPs = make(map[string]bool)
  88. ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
  89. ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
  90. ct.subnetLimit = config.ConnectionsPerCidr
  91. ct.duration = config.Duration
  92. ct.BanDuration = config.BanDuration
  93. ct.BanMessage = config.BanMessage
  94. // assemble exempted nets
  95. for _, cidr := range config.Exempted {
  96. ipaddr := net.ParseIP(cidr)
  97. _, netaddr, err := net.ParseCIDR(cidr)
  98. if ipaddr == nil && err != nil {
  99. return nil, fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
  100. }
  101. if ipaddr != nil {
  102. ct.exemptedIPs[ipaddr.String()] = true
  103. } else {
  104. ct.exemptedNets = append(ct.exemptedNets, *netaddr)
  105. }
  106. }
  107. return &ct, nil
  108. }