package p2p import ( "fmt" "net" "sync" "time" ) type connectionTracker interface { AddConn(net.IP) error RemoveConn(net.IP) Len() int } type connTrackerImpl struct { cache map[string]uint lastConnect map[string]time.Time mutex sync.RWMutex max uint window time.Duration } func newConnTracker(max uint, window time.Duration) connectionTracker { return &connTrackerImpl{ cache: make(map[string]uint), lastConnect: make(map[string]time.Time), max: max, } } func (rat *connTrackerImpl) Len() int { rat.mutex.RLock() defer rat.mutex.RUnlock() return len(rat.cache) } func (rat *connTrackerImpl) AddConn(addr net.IP) error { address := addr.String() rat.mutex.Lock() defer rat.mutex.Unlock() if num := rat.cache[address]; num >= rat.max { return fmt.Errorf("%q has %d connections [max=%d]", address, num, rat.max) } else if num == 0 { // if there is already at least connection, check to // see if it was established before within the window, // and error if so. if last := rat.lastConnect[address]; time.Since(last) < rat.window { return fmt.Errorf("%q tried to connect within window of last %s", address, rat.window) } } rat.cache[address]++ rat.lastConnect[address] = time.Now() return nil } func (rat *connTrackerImpl) RemoveConn(addr net.IP) { address := addr.String() rat.mutex.Lock() defer rat.mutex.Unlock() if num := rat.cache[address]; num > 0 { rat.cache[address]-- } if num := rat.cache[address]; num <= 0 { delete(rat.cache, address) } if last, ok := rat.lastConnect[address]; ok && time.Since(last) > rat.window { delete(rat.lastConnect, address) } }