func TestWriteError(t *testing.T) { var table = []struct { reason, expected string }{ {"hello world", "d14:failure reason11:hello worlde"}, {"what's up", "d14:failure reason9:what's upe"}, } for _, tt := range table { r := httptest.NewRecorder() err := WriteError(r, bittorrent.ClientError(tt.reason)) assert.Nil(t, err) assert.Equal(t, r.Body.String(), tt.expected) } }
// ParseScrape parses an bittorrent.ScrapeRequest from an http.Request. func ParseScrape(r *http.Request) (*bittorrent.ScrapeRequest, error) { qp, err := bittorrent.ParseURLData(r.RequestURI) if err != nil { return nil, err } infoHashes := qp.InfoHashes() if len(infoHashes) < 1 { return nil, bittorrent.ClientError("no info_hash parameter supplied") } request := &bittorrent.ScrapeRequest{ InfoHashes: infoHashes, Params: qp, } return request, nil }
func TestWriteStatus(t *testing.T) { r := httptest.NewRecorder() err := WriteError(r, bittorrent.ClientError("something is missing")) assert.Nil(t, err) assert.Equal(t, r.Body.String(), "d14:failure reason20:something is missinge") }
// Package clientapproval implements a Hook that fails an Announce based on a // whitelist or blacklist of BitTorrent client IDs. package clientapproval import ( "context" "errors" "github.com/chihaya/chihaya/bittorrent" "github.com/chihaya/chihaya/middleware" ) // ErrClientUnapproved is the error returned when a client's PeerID is invalid. var ErrClientUnapproved = bittorrent.ClientError("unapproved client") // Config represents all the values required by this middleware to validate // peers based on their BitTorrent client ID. type Config struct { Whitelist []string `yaml:"whitelist"` Blacklist []string `yaml:"blacklist"` } type hook struct { approved map[bittorrent.ClientID]struct{} unapproved map[bittorrent.ClientID]struct{} } // NewHook returns an instance of the client approval middleware. func NewHook(cfg Config) (middleware.Hook, error) { h := &hook{ approved: make(map[bittorrent.ClientID]struct{}),
"time" jc "github.com/SermoDigital/jose/crypto" "github.com/SermoDigital/jose/jws" "github.com/SermoDigital/jose/jwt" log "github.com/Sirupsen/logrus" "github.com/mendsley/gojwk" "github.com/chihaya/chihaya/bittorrent" "github.com/chihaya/chihaya/middleware" "github.com/chihaya/chihaya/pkg/stopper" ) var ( // ErrMissingJWT is returned when a JWT is missing from a request. ErrMissingJWT = bittorrent.ClientError("unapproved request: missing jwt") // ErrInvalidJWT is returned when a JWT fails to verify. ErrInvalidJWT = bittorrent.ClientError("unapproved request: invalid jwt") ) // Config represents all the values required by this middleware to fetch JWKs // and verify JWTs. type Config struct { Issuer string `yaml:"issuer"` Audience string `yaml:"audience"` JWKSetURL string `yaml:"jwk_set_url"` JWKUpdateInterval time.Duration `yaml:"jwk_set_update_interval"` } type hook struct {
// initialConnectionID is the magic initial connection ID specified by BEP 15. initialConnectionID = []byte{0, 0, 0x04, 0x17, 0x27, 0x10, 0x19, 0x80} // emptyIPs are the value of an IP field that has been left blank. emptyIPv4 = []byte{0, 0, 0, 0} emptyIPv6 = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // eventIDs map values described in BEP 15 to Events. eventIDs = []bittorrent.Event{ bittorrent.None, bittorrent.Completed, bittorrent.Started, bittorrent.Stopped, } errMalformedPacket = bittorrent.ClientError("malformed packet") errMalformedIP = bittorrent.ClientError("malformed IP address") errMalformedEvent = bittorrent.ClientError("malformed event ID") errUnknownAction = bittorrent.ClientError("unknown action ID") errBadConnectionID = bittorrent.ClientError("bad connection ID") errUnknownOptionType = bittorrent.ClientError("unknown option type") ) // ParseAnnounce parses an AnnounceRequest from a UDP request. // // If allowIPSpoofing is true, IPs provided via params will be used. // // If v6 is true the announce will be parsed as an IPv6 announce "the // opentracker way", see // http://opentracker.blog.h3q.com/2007/12/28/the-ipv6-situation/ func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceRequest, error) {
package storage import ( "github.com/chihaya/chihaya/bittorrent" "github.com/chihaya/chihaya/pkg/stopper" ) // ErrResourceDoesNotExist is the error returned by all delete methods in the // store if the requested resource does not exist. var ErrResourceDoesNotExist = bittorrent.ClientError("resource does not exist") // PeerStore is an interface that abstracts the interactions of storing and // manipulating Peers such that it can be implemented for various data stores. type PeerStore interface { // PutSeeder adds a Seeder to the Swarm identified by the provided // infoHash. PutSeeder(infoHash bittorrent.InfoHash, p bittorrent.Peer) error // DeleteSeeder removes a Seeder from the Swarm identified by the // provided infoHash. // // If the Swarm or Peer does not exist, this function should return // ErrResourceDoesNotExist. DeleteSeeder(infoHash bittorrent.InfoHash, p bittorrent.Peer) error // PutLeecher adds a Leecher to the Swarm identified by the provided // infoHash. PutLeecher(infoHash bittorrent.InfoHash, p bittorrent.Peer) error // DeleteLeecher removes a Leecher from the Swarm identified by the // provided infoHash.
// 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 }