func decodePeerKey(pk serializedPeer) bittorrent.Peer { return bittorrent.Peer{ ID: bittorrent.PeerIDFromString(string(pk[:20])), Port: binary.BigEndian.Uint16([]byte(pk[20:22])), IP: net.IP(pk[22:]), } }
// TestPeerStore tests a PeerStore implementation against the interface. func TestPeerStore(t *testing.T, p PeerStore) { testData := []struct { ih bittorrent.InfoHash peer bittorrent.Peer }{ { bittorrent.InfoHashFromString("00000000000000000001"), bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000001"), Port: 1, IP: net.ParseIP("1.1.1.1").To4()}, }, { bittorrent.InfoHashFromString("00000000000000000002"), bittorrent.Peer{ID: bittorrent.PeerIDFromString("00000000000000000002"), Port: 2, IP: net.ParseIP("abab::0001")}, }, } v4Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999994"), IP: net.ParseIP("99.99.99.99").To4(), Port: 9994} v6Peer := bittorrent.Peer{ID: bittorrent.PeerIDFromString("99999999999999999996"), IP: net.ParseIP("fc00::0001"), Port: 9996} for _, c := range testData { peer := v4Peer if len(c.peer.IP) == net.IPv6len { peer = v6Peer } // Test ErrDNE for non-existent swarms. err := p.DeleteLeecher(c.ih, c.peer) require.Equal(t, ErrResourceDoesNotExist, err) err = p.DeleteSeeder(c.ih, c.peer) require.Equal(t, ErrResourceDoesNotExist, err) _, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Equal(t, ErrResourceDoesNotExist, err) // Test empty scrape response for non-existent swarms. scrape := p.ScrapeSwarm(c.ih, len(c.peer.IP) == net.IPv6len) require.Equal(t, uint32(0), scrape.Complete) require.Equal(t, uint32(0), scrape.Incomplete) require.Equal(t, uint32(0), scrape.Snatches) // Insert dummy Peer to keep swarm active // Has the same address family as c.peer err = p.PutLeecher(c.ih, peer) require.Nil(t, err) // Test ErrDNE for non-existent seeder. err = p.DeleteSeeder(c.ih, peer) require.Equal(t, ErrResourceDoesNotExist, err) // Test PutLeecher -> Announce -> DeleteLeecher -> Announce err = p.PutLeecher(c.ih, c.peer) require.Nil(t, err) peers, err := p.AnnouncePeers(c.ih, true, 50, peer) require.Nil(t, err) require.True(t, containsPeer(peers, c.peer)) // non-seeder announce should still return the leecher peers, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Nil(t, err) require.True(t, containsPeer(peers, c.peer)) scrape = p.ScrapeSwarm(c.ih, len(c.peer.IP) == net.IPv6len) require.Equal(t, uint32(2), scrape.Incomplete) require.Equal(t, uint32(0), scrape.Complete) err = p.DeleteLeecher(c.ih, c.peer) require.Nil(t, err) peers, err = p.AnnouncePeers(c.ih, true, 50, peer) require.Nil(t, err) require.False(t, containsPeer(peers, c.peer)) // Test PutSeeder -> Announce -> DeleteSeeder -> Announce err = p.PutSeeder(c.ih, c.peer) require.Nil(t, err) // Should be leecher to see the seeder peers, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Nil(t, err) require.True(t, containsPeer(peers, c.peer)) scrape = p.ScrapeSwarm(c.ih, len(c.peer.IP) == net.IPv6len) require.Equal(t, uint32(1), scrape.Incomplete) require.Equal(t, uint32(1), scrape.Complete) err = p.DeleteSeeder(c.ih, c.peer) require.Nil(t, err) peers, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Nil(t, err) require.False(t, containsPeer(peers, c.peer)) // Test PutLeecher -> Graduate -> Announce -> DeleteLeecher -> Announce err = p.PutLeecher(c.ih, c.peer) require.Nil(t, err) err = p.GraduateLeecher(c.ih, c.peer) require.Nil(t, err) // Has to be leecher to see the graduated seeder peers, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Nil(t, err) require.True(t, containsPeer(peers, c.peer)) // Deleting the Peer as a Leecher should have no effect err = p.DeleteLeecher(c.ih, c.peer) require.Equal(t, ErrResourceDoesNotExist, err) // Verify it's still there peers, err = p.AnnouncePeers(c.ih, false, 50, peer) require.Nil(t, err) require.True(t, containsPeer(peers, c.peer)) // Clean up err = p.DeleteLeecher(c.ih, peer) require.Nil(t, err) // Test ErrDNE for missing leecher err = p.DeleteLeecher(c.ih, peer) require.Equal(t, ErrResourceDoesNotExist, err) err = p.DeleteSeeder(c.ih, c.peer) require.Nil(t, err) err = p.DeleteSeeder(c.ih, c.peer) require.Equal(t, ErrResourceDoesNotExist, err) } }
// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request. // // If allowIPSpoofing is true, IPs provided via params will be used. // If realIPHeader is not empty string, the first value of the HTTP Header with // that name will be used. func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (*bittorrent.AnnounceRequest, error) { qp, err := bittorrent.ParseURLData(r.RequestURI) if err != nil { return nil, err } request := &bittorrent.AnnounceRequest{Params: qp} eventStr, _ := qp.String("event") request.Event, err = bittorrent.NewEvent(eventStr) if err != nil { return nil, bittorrent.ClientError("failed to provide valid client event") } compactStr, _ := qp.String("compact") request.Compact = compactStr != "" && compactStr != "0" infoHashes := qp.InfoHashes() if len(infoHashes) < 1 { return nil, bittorrent.ClientError("no info_hash parameter supplied") } if len(infoHashes) > 1 { return nil, bittorrent.ClientError("multiple info_hash parameters supplied") } request.InfoHash = infoHashes[0] peerID, ok := qp.String("peer_id") if !ok { return nil, bittorrent.ClientError("failed to parse parameter: peer_id") } if len(peerID) != 20 { return nil, bittorrent.ClientError("failed to provide valid peer_id") } request.Peer.ID = bittorrent.PeerIDFromString(peerID) request.Left, err = qp.Uint64("left") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: left") } request.Downloaded, err = qp.Uint64("downloaded") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: downloaded") } request.Uploaded, err = qp.Uint64("uploaded") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: uploaded") } numwant, err := qp.Uint64("numwant") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: numwant") } request.NumWant = uint32(numwant) port, err := qp.Uint64("port") if err != nil { return nil, bittorrent.ClientError("failed to parse parameter: port") } request.Peer.Port = uint16(port) request.Peer.IP = requestedIP(r, qp, realIPHeader, allowIPSpoofing) if request.Peer.IP == nil { return nil, bittorrent.ClientError("failed to parse peer IP address") } // Sanitize IPv4 addresses to 4 bytes. if ip := request.Peer.IP.To4(); ip != nil { request.Peer.IP = ip } return request, nil }