From 8acfc5149b2ef9f47a362bf193893c30673b287e Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Fri, 15 Jun 2018 22:38:11 +1000 Subject: [PATCH] Rate limit incoming IP prefixes Lots of bad or dishonest incoming handshakes for unwanted torrents. --- client.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/client.go b/client.go index 8c40226b..d4c08422 100644 --- a/client.go +++ b/client.go @@ -65,8 +65,12 @@ type Client struct { torrents map[metainfo.Hash]*Torrent // An aggregate of stats over all connections. stats ConnStats + + acceptLimiter map[ipStr]int } +type ipStr string + func (cl *Client) BadPeerIPs() []string { cl.mu.RLock() defer cl.mu.RUnlock() @@ -184,6 +188,7 @@ func NewClient(cfg *Config) (cl *Client, err error) { dopplegangerAddrs: make(map[string]struct{}), torrents: make(map[metainfo.Hash]*Torrent), } + go cl.acceptLimitClearer() cl.initLogger() defer func() { if err == nil { @@ -374,6 +379,9 @@ func (cl *Client) rejectAccepted(conn net.Conn) bool { if cl.config.DisableIPv6 && len(rip) == net.IPv6len && rip.To4() == nil { return true } + if cl.rateLimitAccept(rip) { + return true + } return cl.badPeerIPPort(rip, missinggo.AddrPort(ra)) } @@ -759,9 +767,15 @@ func (cl *Client) runReceivedConn(c *connection) { t, err := cl.receiveHandshakes(c) if err != nil { log.Fmsg("error receiving handshakes: %s", err).AddValue(debugLogValue).Add("network", c.remoteAddr().Network()).Log(cl.logger) + cl.mu.Lock() + cl.onBadAccept(c.remoteAddr()) + cl.mu.Unlock() return } if t == nil { + cl.mu.Lock() + cl.onBadAccept(c.remoteAddr()) + cl.mu.Unlock() return } cl.mu.Lock() @@ -1239,3 +1253,36 @@ func (cl *Client) ListenAddrs() (ret []net.Addr) { }) return } + +func (cl *Client) onBadAccept(addr net.Addr) { + ip := maskIpForAcceptLimiting(missinggo.AddrIP(addr)) + if cl.acceptLimiter == nil { + cl.acceptLimiter = make(map[ipStr]int) + } + cl.acceptLimiter[ipStr(ip.String())]++ +} + +func maskIpForAcceptLimiting(ip net.IP) net.IP { + if ip4 := ip.To4(); ip4 != nil { + return ip4.Mask(net.CIDRMask(24, 32)) + } + return ip +} + +func (cl *Client) acceptLimitClearer() { + for { + select { + case <-cl.closed.LockedChan(&cl.mu): + return + case <-time.After(15 * time.Minute): + cl.mu.Lock() + cl.acceptLimiter = nil + cl.mu.Unlock() + } + } +} + +func (cl *Client) rateLimitAccept(ip net.IP) bool { + // return true + return cl.acceptLimiter[ipStr(maskIpForAcceptLimiting(ip).String())] >= 10 +}