2022-01-10 13:19:16 +08:00
|
|
|
package torrent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/sha1"
|
2022-01-12 11:23:30 +08:00
|
|
|
"net/netip"
|
2022-01-10 13:19:16 +08:00
|
|
|
|
2022-01-21 19:28:15 +08:00
|
|
|
"github.com/anacrolix/generics"
|
2022-01-10 13:19:16 +08:00
|
|
|
"github.com/anacrolix/torrent/smartban"
|
|
|
|
)
|
|
|
|
|
2022-01-12 11:23:30 +08:00
|
|
|
type bannableAddr = netip.Addr
|
2022-01-10 13:19:16 +08:00
|
|
|
|
2022-01-12 11:23:30 +08:00
|
|
|
type smartBanCache = smartban.Cache[bannableAddr, RequestIndex, [sha1.Size]byte]
|
2022-01-10 13:19:16 +08:00
|
|
|
|
|
|
|
type blockCheckingWriter struct {
|
|
|
|
cache *smartBanCache
|
|
|
|
requestIndex RequestIndex
|
|
|
|
// Peers that didn't match blocks written now.
|
2022-01-12 11:23:30 +08:00
|
|
|
badPeers map[bannableAddr]struct{}
|
2022-01-10 13:19:16 +08:00
|
|
|
blockBuffer bytes.Buffer
|
|
|
|
chunkSize int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (me *blockCheckingWriter) checkBlock() {
|
|
|
|
b := me.blockBuffer.Next(me.chunkSize)
|
|
|
|
for _, peer := range me.cache.CheckBlock(me.requestIndex, b) {
|
2022-01-12 11:23:30 +08:00
|
|
|
generics.MakeMapIfNilAndSet(&me.badPeers, peer, struct{}{})
|
2022-01-10 13:19:16 +08:00
|
|
|
}
|
|
|
|
me.requestIndex++
|
|
|
|
}
|
|
|
|
|
|
|
|
func (me *blockCheckingWriter) checkFullBlocks() {
|
|
|
|
for me.blockBuffer.Len() >= me.chunkSize {
|
|
|
|
me.checkBlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (me *blockCheckingWriter) Write(b []byte) (int, error) {
|
|
|
|
n, err := me.blockBuffer.Write(b)
|
|
|
|
if err != nil {
|
|
|
|
// bytes.Buffer.Write should never fail.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
me.checkFullBlocks()
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check any remaining block data. Terminal pieces or piece sizes that don't divide into the chunk
|
|
|
|
// size cleanly may leave fragments that should be checked.
|
|
|
|
func (me *blockCheckingWriter) Flush() {
|
|
|
|
for me.blockBuffer.Len() != 0 {
|
|
|
|
me.checkBlock()
|
|
|
|
}
|
|
|
|
}
|