diff --git a/chihaya.go b/chihaya.go index 8a7386b..9470485 100644 --- a/chihaya.go +++ b/chihaya.go @@ -12,10 +12,62 @@ import ( ) // PeerID represents a peer ID. -type PeerID string +type PeerID [20]byte -// InfoHash represents an infohash in hexadecimal notation. -type InfoHash string +// PeerIDFromBytes creates a PeerID from a byte slice. +// +// It panics if b is not 20 bytes long. +func PeerIDFromBytes(b []byte) PeerID { + if len(b) != 20 { + panic("peer ID must be 20 bytes") + } + + var buf [20]byte + copy(buf[:], b) + return PeerID(buf) +} + +// PeerIDFromString creates a PeerID from a string. +// +// It panics if s is not 20 bytes long. +func PeerIDFromString(s string) PeerID { + if len(s) != 20 { + panic("peer ID must be 20 bytes") + } + + var buf [20]byte + copy(buf[:], s) + return PeerID(buf) +} + +// InfoHash represents an infohash. +type InfoHash [20]byte + +// InfoHashFromBytes creates an InfoHash from a byte slice. +// +// It panics if b is not 20 bytes long. +func InfoHashFromBytes(b []byte) InfoHash { + if len(b) != 20 { + panic("infohash must be 20 bytes") + } + + var buf [20]byte + copy(buf[:], b) + return InfoHash(buf) +} + +// InfoHashFromString creates an InfoHash from a string. +// +// It panics if s is not 20 bytes long. +func InfoHashFromString(s string) InfoHash { + if len(s) != 20 { + panic("infohash must be 20 bytes") + } + + var buf [20]byte + copy(buf[:], s) + return InfoHash(buf) +} // AnnounceRequest represents the parsed parameters from an announce request. type AnnounceRequest struct { diff --git a/chihaya_test.go b/chihaya_test.go index 8b2d5b9..6fd513e 100644 --- a/chihaya_test.go +++ b/chihaya_test.go @@ -30,7 +30,7 @@ func TestPeerEquality(t *testing.T) { var builtPeers []Peer for _, peer := range peers { builtPeers = append(builtPeers, Peer{ - ID: PeerID(peer.peerID), + ID: PeerIDFromString(peer.peerID), IP: net.ParseIP(peer.ip), Port: peer.port, }) diff --git a/middleware/deniability/deniability_test.go b/middleware/deniability/deniability_test.go index dbef933..4492898 100644 --- a/middleware/deniability/deniability_test.go +++ b/middleware/deniability/deniability_test.go @@ -42,7 +42,7 @@ func TestReplacePeer(t *testing.T) { cfg: &cfg, } peer := chihaya.Peer{ - ID: chihaya.PeerID("abcdefghijklmnoprstu"), + ID: chihaya.PeerID([20]byte{}), Port: 2000, IP: net.ParseIP("10.150.255.23"), } @@ -77,7 +77,7 @@ func TestInsertPeer(t *testing.T) { cfg: &cfg, } peer := chihaya.Peer{ - ID: chihaya.PeerID("abcdefghijklmnoprstu"), + ID: chihaya.PeerID([20]byte{}), Port: 2000, IP: net.ParseIP("10.150.255.23"), } diff --git a/pkg/random/peer.go b/pkg/random/peer.go index c950221..c58a11f 100644 --- a/pkg/random/peer.go +++ b/pkg/random/peer.go @@ -67,7 +67,7 @@ func Peer(r *rand.Rand, prefix string, v6 bool, minPort, maxPort int) chihaya.Pe prefix = prefix + AlphaNumericString(r, 20-len(prefix)) return chihaya.Peer{ - ID: chihaya.PeerID(prefix), + ID: chihaya.PeerIDFromString(prefix), Port: port, IP: ip, } diff --git a/pkg/random/peer_test.go b/pkg/random/peer_test.go index f3c883c..636f42d 100644 --- a/pkg/random/peer_test.go +++ b/pkg/random/peer_test.go @@ -34,10 +34,10 @@ func TestPeer(t *testing.T) { } p := Peer(r, "abcdefghijklmnopqrst", false, 2000, 2000) - assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID)) + assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID[:])) assert.Equal(t, uint16(2000), p.Port) p = Peer(r, "abcdefghijklmnopqrstUVWXYZ", true, -10, -5) - assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID)) + assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID[:])) assert.True(t, p.Port >= uint16(1) && p.Port <= uint16(65535)) } diff --git a/server/http/query/query.go b/server/http/query/query.go index 5f51555..e8fe6e8 100644 --- a/server/http/query/query.go +++ b/server/http/query/query.go @@ -20,6 +20,10 @@ import ( // it. var ErrKeyNotFound = errors.New("query: value for the provided key does not exist") +// ErrInvalidInfohash is returned when parsing a query encounters an infohash +// with invalid length. +var ErrInvalidInfohash = errors.New("query: invalid infohash") + // Query represents a parsed URL.Query. type Query struct { query string @@ -71,7 +75,10 @@ func New(query string) (*Query, error) { } if keyStr == "info_hash" { - q.infoHashes = append(q.infoHashes, chihaya.InfoHash(valStr)) + if len(valStr) != 20 { + return nil, ErrInvalidInfohash + } + q.infoHashes = append(q.infoHashes, chihaya.InfoHashFromString(valStr)) } else { q.params[strings.ToLower(keyStr)] = valStr } diff --git a/server/http/request.go b/server/http/request.go index 354c3b7..5d17aad 100644 --- a/server/http/request.go +++ b/server/http/request.go @@ -49,7 +49,10 @@ func announceRequest(r *http.Request, cfg *httpConfig) (*chihaya.AnnounceRequest if err != nil { return nil, tracker.ClientError("failed to parse parameter: peer_id") } - request.PeerID = chihaya.PeerID(peerID) + if len(peerID) != 20 { + return nil, tracker.ClientError("failed to provide valid peer_id") + } + request.PeerID = chihaya.PeerIDFromString(peerID) request.Left, err = q.Uint64("left") if err != nil { diff --git a/server/http/writer.go b/server/http/writer.go index 5f9eda0..5de6c3d 100644 --- a/server/http/writer.go +++ b/server/http/writer.go @@ -71,7 +71,7 @@ func writeAnnounceResponse(w http.ResponseWriter, resp *chihaya.AnnounceResponse func writeScrapeResponse(w http.ResponseWriter, resp *chihaya.ScrapeResponse) error { filesDict := bencode.NewDict() for infohash, scrape := range resp.Files { - filesDict[string(infohash)] = bencode.Dict{ + filesDict[string(infohash[:])] = bencode.Dict{ "complete": scrape.Complete, "incomplete": scrape.Incomplete, } @@ -91,7 +91,7 @@ func compact(peer chihaya.Peer) (buf []byte) { func dict(peer chihaya.Peer) bencode.Dict { return bencode.Dict{ - "peer id": string(peer.ID), + "peer id": string(peer.ID[:]), "ip": peer.IP.String(), "port": peer.Port, } diff --git a/server/store/memory/peer_store.go b/server/store/memory/peer_store.go index 6638940..7ab1cd9 100644 --- a/server/store/memory/peer_store.go +++ b/server/store/memory/peer_store.go @@ -78,20 +78,20 @@ var _ store.PeerStore = &peerStore{} func (s *peerStore) shardIndex(infoHash chihaya.InfoHash) uint32 { idx := fnv.New32() - idx.Write([]byte(infoHash)) + idx.Write(infoHash[:]) return idx.Sum32() % uint32(len(s.shards)) } func peerKey(p chihaya.Peer) string { - return string(p.IP) + string(p.ID) + return string(p.IP) + string(p.ID[:]) } func seedersKey(infoHash chihaya.InfoHash) string { - return string(infoHash) + "-s" + return string(infoHash[:]) + "-s" } func leechersKey(infoHash chihaya.InfoHash) string { - return string(infoHash) + "-l" + return string(infoHash[:]) + "-l" } func (s *peerStore) PutSeeder(infoHash chihaya.InfoHash, p chihaya.Peer) error { diff --git a/server/store/memory/peer_store_test.go b/server/store/memory/peer_store_test.go index 157e00e..8eb6a0c 100644 --- a/server/store/memory/peer_store_test.go +++ b/server/store/memory/peer_store_test.go @@ -25,7 +25,7 @@ func peerInSlice(peer chihaya.Peer, peers []chihaya.Peer) bool { func TestPeerStoreAPI(t *testing.T) { var ( - hash = chihaya.InfoHash("11111111111111111111") + hash = chihaya.InfoHash([20]byte{}) peers = []struct { seeder bool @@ -62,7 +62,7 @@ func TestPeerStoreAPI(t *testing.T) { for _, p := range peers { // Construct chihaya.Peer from test data. peer := chihaya.Peer{ - ID: chihaya.PeerID(p.peerID), + ID: chihaya.PeerIDFromString(p.peerID), IP: net.ParseIP(p.ip), Port: p.port, } @@ -95,7 +95,7 @@ func TestPeerStoreAPI(t *testing.T) { for _, p := range peers { // Construct chihaya.Peer from test data. peer := chihaya.Peer{ - ID: chihaya.PeerID(p.peerID), + ID: chihaya.PeerIDFromString(p.peerID), IP: net.ParseIP(p.ip), Port: p.port, } @@ -121,7 +121,7 @@ func TestPeerStoreAPI(t *testing.T) { for _, p := range peers { // Construct chihaya.Peer from test data. peer := chihaya.Peer{ - ID: chihaya.PeerID(p.peerID), + ID: chihaya.PeerIDFromString(p.peerID), IP: net.ParseIP(p.ip), Port: p.port, } @@ -136,7 +136,7 @@ func TestPeerStoreAPI(t *testing.T) { assert.Equal(t, 6, s.NumSeeders(hash)) assert.Equal(t, 4, s.NumLeechers(hash)) peer := chihaya.Peer{ - ID: chihaya.PeerID(peers[0].peerID), + ID: chihaya.PeerIDFromString(peers[0].peerID), IP: net.ParseIP(peers[0].ip), Port: peers[0].port, } diff --git a/server/store/middleware/client/blacklist.go b/server/store/middleware/client/blacklist.go index ed675f9..e994d2f 100644 --- a/server/store/middleware/client/blacklist.go +++ b/server/store/middleware/client/blacklist.go @@ -23,7 +23,7 @@ var ErrBlacklistedClient = tracker.ClientError("client blacklisted") // announce that are not stored in the StringStore. func blacklistAnnounceClient(next tracker.AnnounceHandler) tracker.AnnounceHandler { return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - blacklisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID))) + blacklisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID[:]))) if err != nil { return err } else if blacklisted { diff --git a/server/store/middleware/client/whitelist.go b/server/store/middleware/client/whitelist.go index 980f6cd..275b56e 100644 --- a/server/store/middleware/client/whitelist.go +++ b/server/store/middleware/client/whitelist.go @@ -26,7 +26,7 @@ var ErrNotWhitelistedClient = tracker.ClientError("client not whitelisted") // announce that are stored in the StringStore. func whitelistAnnounceClient(next tracker.AnnounceHandler) tracker.AnnounceHandler { return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - whitelisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID))) + whitelisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID[:]))) if err != nil { return err } else if !whitelisted { diff --git a/server/store/middleware/infohash/blacklist.go b/server/store/middleware/infohash/blacklist.go index 3595748..9a24320 100644 --- a/server/store/middleware/infohash/blacklist.go +++ b/server/store/middleware/infohash/blacklist.go @@ -28,7 +28,7 @@ var mustGetStore func() store.StringStore // for infohashes that are not stored in a StringStore. func blacklistAnnounceInfohash(next tracker.AnnounceHandler) tracker.AnnounceHandler { return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - blacklisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash)) + blacklisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash[:])) if err != nil { return err } else if blacklisted { @@ -72,7 +72,7 @@ func blacklistFilterScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { infohashes := req.InfoHashes for i, ih := range infohashes { - blacklisted, err = storage.HasString(PrefixInfohash + string(ih)) + blacklisted, err = storage.HasString(PrefixInfohash + string(ih[:])) if err != nil { return err @@ -92,7 +92,7 @@ func blacklistBlockScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { storage := mustGetStore() for _, ih := range req.InfoHashes { - blacklisted, err = storage.HasString(PrefixInfohash + string(ih)) + blacklisted, err = storage.HasString(PrefixInfohash + string(ih[:])) if err != nil { return err diff --git a/server/store/middleware/infohash/blacklist_test.go b/server/store/middleware/infohash/blacklist_test.go index cc06906..804a336 100644 --- a/server/store/middleware/infohash/blacklist_test.go +++ b/server/store/middleware/infohash/blacklist_test.go @@ -40,12 +40,17 @@ var mock store.StringStore = &storeMock{ strings: make(map[string]struct{}), } +var ( + ih1 = chihaya.InfoHash([20]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + ih2 = chihaya.InfoHash([20]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) +) + func TestASetUp(t *testing.T) { mustGetStore = func() store.StringStore { return mock } - mustGetStore().PutString(PrefixInfohash + "abc") + mustGetStore().PutString(PrefixInfohash + string(ih1[:])) } func TestBlacklistAnnounceMiddleware(t *testing.T) { @@ -61,11 +66,11 @@ func TestBlacklistAnnounceMiddleware(t *testing.T) { err := handler(nil, &req, &resp) assert.Nil(t, err) - req.InfoHash = chihaya.InfoHash("abc") + req.InfoHash = chihaya.InfoHash(ih1) err = handler(nil, &req, &resp) assert.Equal(t, ErrBlockedInfohash, err) - req.InfoHash = chihaya.InfoHash("def") + req.InfoHash = chihaya.InfoHash(ih2) err = handler(nil, &req, &resp) assert.Nil(t, err) } @@ -90,11 +95,11 @@ func TestBlacklistScrapeMiddlewareBlock(t *testing.T) { err = handler(nil, &req, &resp) assert.Nil(t, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc"), chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Equal(t, ErrBlockedInfohash, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Nil(t, err) } @@ -119,12 +124,12 @@ func TestBlacklistScrapeMiddlewareFilter(t *testing.T) { err = handler(nil, &req, &resp) assert.Nil(t, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc"), chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash("def")}, req.InfoHashes) + assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih2)}, req.InfoHashes) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Nil(t, err) } diff --git a/server/store/middleware/infohash/whitelist.go b/server/store/middleware/infohash/whitelist.go index 85dec0c..99caeda 100644 --- a/server/store/middleware/infohash/whitelist.go +++ b/server/store/middleware/infohash/whitelist.go @@ -21,7 +21,7 @@ const PrefixInfohash = "ih-" // for infohashes that are not stored in a StringStore func whitelistAnnounceInfohash(next tracker.AnnounceHandler) tracker.AnnounceHandler { return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - whitelisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash)) + whitelisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash[:])) if err != nil { return err @@ -65,7 +65,7 @@ func whitelistFilterScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { infohashes := req.InfoHashes for i, ih := range infohashes { - whitelisted, err = storage.HasString(PrefixInfohash + string(ih)) + whitelisted, err = storage.HasString(PrefixInfohash + string(ih[:])) if err != nil { return err @@ -85,7 +85,7 @@ func whitelistBlockScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { storage := mustGetStore() for _, ih := range req.InfoHashes { - whitelisted, err = storage.HasString(PrefixInfohash + string(ih)) + whitelisted, err = storage.HasString(PrefixInfohash + string(ih[:])) if err != nil { return err diff --git a/server/store/middleware/infohash/whitelist_test.go b/server/store/middleware/infohash/whitelist_test.go index f958638..3a68386 100644 --- a/server/store/middleware/infohash/whitelist_test.go +++ b/server/store/middleware/infohash/whitelist_test.go @@ -26,11 +26,11 @@ func TestWhitelistAnnounceMiddleware(t *testing.T) { err := handler(nil, &req, &resp) assert.Equal(t, ErrBlockedInfohash, err) - req.InfoHash = chihaya.InfoHash("def") + req.InfoHash = chihaya.InfoHash(ih2) err = handler(nil, &req, &resp) assert.Equal(t, ErrBlockedInfohash, err) - req.InfoHash = chihaya.InfoHash("abc") + req.InfoHash = chihaya.InfoHash(ih1) err = handler(nil, &req, &resp) assert.Nil(t, err) } @@ -55,11 +55,11 @@ func TestWhitelistScrapeMiddlewareBlock(t *testing.T) { err = handler(nil, &req, &resp) assert.Nil(t, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc"), chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Equal(t, ErrBlockedInfohash, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1)} err = handler(nil, &req, &resp) assert.Nil(t, err) } @@ -84,13 +84,13 @@ func TestWhitelistScrapeMiddlewareFilter(t *testing.T) { err = handler(nil, &req, &resp) assert.Nil(t, err) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc"), chihaya.InfoHash("def")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} err = handler(nil, &req, &resp) assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash("abc")}, req.InfoHashes) + assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih1)}, req.InfoHashes) - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash("abc")} + req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1)} err = handler(nil, &req, &resp) assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash("abc")}, req.InfoHashes) + assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih1)}, req.InfoHashes) }