diff --git a/.golangci-lint.yaml b/.golangci.yaml similarity index 90% rename from .golangci-lint.yaml rename to .golangci.yaml index 15f497c..8f6fe3b 100644 --- a/.golangci-lint.yaml +++ b/.golangci.yaml @@ -6,6 +6,9 @@ output: linters-settings: goimports: local-prefixes: "github.com/chihaya/chihaya" + gosec: + excludes: + - "G404" # Allow the usage of math/rand linters: enable: - "bidichk" @@ -26,7 +29,6 @@ linters: - "makezero" - "prealloc" - "predeclared" - - "promlinter" - "revive" - "rowserrcheck" - "staticcheck" diff --git a/cmd/chihaya/main.go b/cmd/chihaya/main.go index 1373f63..bfb0997 100644 --- a/cmd/chihaya/main.go +++ b/cmd/chihaya/main.go @@ -100,7 +100,7 @@ func (r *Run) Start(ps storage.PeerStore) error { } func combineErrors(prefix string, errs []error) error { - var errStrs []string + errStrs := make([]string, 0, len(errs)) for _, err := range errs { errStrs = append(errStrs, err.Error()) } diff --git a/frontend/http/bencode/encoder.go b/frontend/http/bencode/encoder.go index d9bcaa7..44330dc 100644 --- a/frontend/http/bencode/encoder.go +++ b/frontend/http/bencode/encoder.go @@ -66,7 +66,7 @@ func marshal(w io.Writer, data interface{}) (err error) { err = marshalInt(w, int64(v)) case int64: - err = marshalInt(w, int64(v)) + err = marshalInt(w, v) case uint: err = marshalUint(w, uint64(v)) @@ -78,7 +78,7 @@ func marshal(w io.Writer, data interface{}) (err error) { err = marshalUint(w, uint64(v)) case uint64: - err = marshalUint(w, uint64(v)) + err = marshalUint(w, v) case time.Duration: // Assume seconds err = marshalInt(w, int64(v/time.Second)) diff --git a/frontend/http/frontend.go b/frontend/http/frontend.go index 2704788..8854f0f 100644 --- a/frontend/http/frontend.go +++ b/frontend/http/frontend.go @@ -164,6 +164,7 @@ func NewFrontend(logic frontend.TrackerLogic, provided Config) (*Frontend, error if cfg.TLSCertPath != "" && cfg.TLSKeyPath != "" { var err error f.tlsCfg = &tls.Config{ + MinVersion: tls.VersionTLS12, Certificates: make([]tls.Certificate, 1), } f.tlsCfg.Certificates[0], err = tls.LoadX509KeyPair(cfg.TLSCertPath, cfg.TLSKeyPath) @@ -265,7 +266,7 @@ func (f *Frontend) serveHTTP(l net.Listener) error { f.srv.SetKeepAlivesEnabled(f.EnableKeepAlive) // Start the HTTP server. - if err := f.srv.Serve(l); err != http.ErrServerClosed { + if err := f.srv.Serve(l); !errors.Is(err, http.ErrServerClosed) { return err } return nil @@ -285,7 +286,7 @@ func (f *Frontend) serveHTTPS(l net.Listener) error { f.tlsSrv.SetKeepAlivesEnabled(f.EnableKeepAlive) // Start the HTTP server. - if err := f.tlsSrv.ServeTLS(l, "", ""); err != http.ErrServerClosed { + if err := f.tlsSrv.ServeTLS(l, "", ""); !errors.Is(err, http.ErrServerClosed) { return err } return nil diff --git a/frontend/http/parser.go b/frontend/http/parser.go index e690ec2..aac161c 100644 --- a/frontend/http/parser.go +++ b/frontend/http/parser.go @@ -1,6 +1,7 @@ package http import ( + "errors" "net" "net/http" @@ -92,7 +93,7 @@ func ParseAnnounce(r *http.Request, opts ParseOptions) (*bittorrent.AnnounceRequ // Determine the number of peers the client wants in the response. numwant, err := qp.Uint64("numwant", 32) - if err != nil && err != bittorrent.ErrKeyNotFound { + if err != nil && !errors.Is(err, bittorrent.ErrKeyNotFound) { return nil, bittorrent.ClientError("failed to parse parameter: numwant") } // If there were no errors, the user actually provided the numwant. diff --git a/frontend/http/prometheus.go b/frontend/http/prometheus.go index e82b1e1..d866b97 100644 --- a/frontend/http/prometheus.go +++ b/frontend/http/prometheus.go @@ -1,6 +1,7 @@ package http import ( + "errors" "time" "github.com/prometheus/client_golang/prometheus" @@ -26,8 +27,9 @@ var promResponseDurationMilliseconds = prometheus.NewHistogramVec( func recordResponseDuration(action string, af *bittorrent.AddressFamily, err error, duration time.Duration) { var errString string if err != nil { - if _, ok := err.(bittorrent.ClientError); ok { - errString = err.Error() + var clientErr bittorrent.ClientError + if errors.As(err, &clientErr) { + errString = clientErr.Error() } else { errString = "internal error" } diff --git a/frontend/http/writer.go b/frontend/http/writer.go index 8854762..658d968 100644 --- a/frontend/http/writer.go +++ b/frontend/http/writer.go @@ -1,6 +1,7 @@ package http import ( + "errors" "net/http" "github.com/chihaya/chihaya/bittorrent" @@ -11,8 +12,9 @@ import ( // WriteError communicates an error to a BitTorrent client over HTTP. func WriteError(w http.ResponseWriter, err error) error { message := "internal server error" - if _, clientErr := err.(bittorrent.ClientError); clientErr { - message = err.Error() + var clientErr bittorrent.ClientError + if errors.As(err, &clientErr) { + message = clientErr.Error() } else { log.Error("http: internal error", log.Err(err)) } @@ -57,7 +59,7 @@ func WriteAnnounceResponse(w http.ResponseWriter, resp *bittorrent.AnnounceRespo } // Add the peers to the dictionary. - var peers []bencode.Dict + peers := make([]bencode.Dict, 0, len(resp.IPv4Peers)+len(resp.IPv6Peers)) for _, peer := range resp.IPv4Peers { peers = append(peers, dict(peer)) } diff --git a/frontend/udp/frontend.go b/frontend/udp/frontend.go index 40a1a2d..a9fb482 100644 --- a/frontend/udp/frontend.go +++ b/frontend/udp/frontend.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "encoding/binary" + "errors" "fmt" "math/rand" "net" @@ -123,8 +124,7 @@ func NewFrontend(logic frontend.TrackerLogic, provided Config) (*Frontend, error }, } - err := f.listen() - if err != nil { + if err := f.listen(); err != nil { return nil, err } @@ -188,7 +188,8 @@ func (t *Frontend) serve() error { n, addr, err := t.socket.ReadFromUDP(*buffer) if err != nil { pool.Put(buffer) - if netErr, ok := err.(net.Error); ok && netErr.Temporary() { + var netErr net.Error + if errors.As(err, &netErr); netErr.Temporary() { // A temporary failure is not fatal; just pretend it never happened. continue } diff --git a/frontend/udp/parser.go b/frontend/udp/parser.go index 85a5eb1..4bed120 100644 --- a/frontend/udp/parser.go +++ b/frontend/udp/parser.go @@ -115,7 +115,7 @@ func ParseAnnounce(r Request, v6Action bool, opts ParseOptions) (*bittorrent.Ann request := &bittorrent.AnnounceRequest{ Event: eventIDs[eventID], InfoHash: bittorrent.InfoHashFromBytes(infohash), - NumWant: uint32(numWant), + NumWant: numWant, Left: left, Downloaded: downloaded, Uploaded: uploaded, diff --git a/frontend/udp/parser_test.go b/frontend/udp/parser_test.go index 4125258..585327f 100644 --- a/frontend/udp/parser_test.go +++ b/frontend/udp/parser_test.go @@ -1,6 +1,7 @@ package udp import ( + "errors" "fmt" "testing" ) @@ -51,7 +52,7 @@ func TestHandleOptionalParameters(t *testing.T) { for _, tt := range table { t.Run(fmt.Sprintf("%#v as %#v", tt.data, tt.values), func(t *testing.T) { params, err := handleOptionalParameters(tt.data) - if err != tt.err { + if !errors.Is(err, tt.err) { if tt.err == nil { t.Fatalf("expected no parsing error for %x but got %s", tt.data, err) } else { diff --git a/frontend/udp/prometheus.go b/frontend/udp/prometheus.go index 4b1f5ba..3db296f 100644 --- a/frontend/udp/prometheus.go +++ b/frontend/udp/prometheus.go @@ -1,6 +1,7 @@ package udp import ( + "errors" "time" "github.com/prometheus/client_golang/prometheus" @@ -26,8 +27,9 @@ var promResponseDurationMilliseconds = prometheus.NewHistogramVec( func recordResponseDuration(action string, af *bittorrent.AddressFamily, err error, duration time.Duration) { var errString string if err != nil { - if _, ok := err.(bittorrent.ClientError); ok { - errString = err.Error() + var clientErr bittorrent.ClientError + if errors.As(err, &clientErr) { + errString = clientErr.Error() } else { errString = "internal error" } diff --git a/frontend/udp/writer.go b/frontend/udp/writer.go index a26db64..12331f1 100644 --- a/frontend/udp/writer.go +++ b/frontend/udp/writer.go @@ -2,6 +2,7 @@ package udp import ( "encoding/binary" + "errors" "fmt" "io" "time" @@ -12,8 +13,9 @@ import ( // WriteError writes the failure reason as a null-terminated string. func WriteError(w io.Writer, txID []byte, err error) { // If the client wasn't at fault, acknowledge it. - if _, ok := err.(bittorrent.ClientError); !ok { - err = fmt.Errorf("internal error occurred: %s", err.Error()) + var clientErr bittorrent.ClientError + if !errors.As(err, &clientErr) { + err = fmt.Errorf("internal error occurred: %w", err) } buf := newBuffer() diff --git a/middleware/clientapproval/clientapproval.go b/middleware/clientapproval/clientapproval.go index 51c2a55..28d8e4d 100644 --- a/middleware/clientapproval/clientapproval.go +++ b/middleware/clientapproval/clientapproval.go @@ -28,7 +28,7 @@ func (d driver) NewHook(optionBytes []byte) (middleware.Hook, error) { var cfg Config err := yaml.Unmarshal(optionBytes, &cfg) if err != nil { - return nil, fmt.Errorf("invalid options for middleware %s: %s", Name, err) + return nil, fmt.Errorf("invalid options for middleware %s: %w", Name, err) } return NewHook(cfg) diff --git a/middleware/hooks.go b/middleware/hooks.go index 4649f28..9f3e1a1 100644 --- a/middleware/hooks.go +++ b/middleware/hooks.go @@ -2,6 +2,7 @@ package middleware import ( "context" + "errors" "github.com/chihaya/chihaya/bittorrent" "github.com/chihaya/chihaya/storage" @@ -37,12 +38,12 @@ func (h *swarmInteractionHook) HandleAnnounce(ctx context.Context, req *bittorre switch { case req.Event == bittorrent.Stopped: err = h.store.DeleteSeeder(req.InfoHash, req.Peer) - if err != nil && err != storage.ErrResourceDoesNotExist { + if err != nil && !errors.Is(err, storage.ErrResourceDoesNotExist) { return ctx, err } err = h.store.DeleteLeecher(req.InfoHash, req.Peer) - if err != nil && err != storage.ErrResourceDoesNotExist { + if err != nil && !errors.Is(err, storage.ErrResourceDoesNotExist) { return ctx, err } case req.Event == bittorrent.Completed: @@ -106,7 +107,7 @@ func (h *responseHook) HandleAnnounce(ctx context.Context, req *bittorrent.Annou func (h *responseHook) appendPeers(req *bittorrent.AnnounceRequest, resp *bittorrent.AnnounceResponse) error { seeding := req.Left == 0 peers, err := h.store.AnnouncePeers(req.InfoHash, seeding, int(req.NumWant), req.Peer) - if err != nil && err != storage.ErrResourceDoesNotExist { + if err != nil && !errors.Is(err, storage.ErrResourceDoesNotExist) { return err } diff --git a/middleware/jwt/jwt.go b/middleware/jwt/jwt.go index f8c760a..2f5bf03 100644 --- a/middleware/jwt/jwt.go +++ b/middleware/jwt/jwt.go @@ -44,7 +44,7 @@ func (d driver) NewHook(optionBytes []byte) (middleware.Hook, error) { var cfg Config err := yaml.Unmarshal(optionBytes, &cfg) if err != nil { - return nil, fmt.Errorf("invalid options for middleware %s: %s", Name, err) + return nil, fmt.Errorf("invalid options for middleware %s: %w", Name, err) } return NewHook(cfg) @@ -93,8 +93,7 @@ func NewHook(cfg Config) (middleware.Hook, error) { } log.Debug("performing initial fetch of JWKs") - err := h.updateKeys() - if err != nil { + if err := h.updateKeys(); err != nil { return nil, errors.New("failed to fetch initial JWK Set: " + err.Error()) } diff --git a/middleware/pkg/random/entropy.go b/middleware/pkg/random/entropy.go index d62fed8..8ee44a3 100644 --- a/middleware/pkg/random/entropy.go +++ b/middleware/pkg/random/entropy.go @@ -11,7 +11,7 @@ import ( // // Calling DeriveEntropyFromRequest multiple times yields the same values. func DeriveEntropyFromRequest(req *bittorrent.AnnounceRequest) (uint64, uint64) { - v0 := binary.BigEndian.Uint64([]byte(req.InfoHash[:8])) + binary.BigEndian.Uint64([]byte(req.InfoHash[8:16])) - v1 := binary.BigEndian.Uint64([]byte(req.Peer.ID[:8])) + binary.BigEndian.Uint64([]byte(req.Peer.ID[8:16])) + v0 := binary.BigEndian.Uint64(req.InfoHash[:8]) + binary.BigEndian.Uint64(req.InfoHash[8:16]) + v1 := binary.BigEndian.Uint64(req.Peer.ID[:8]) + binary.BigEndian.Uint64(req.Peer.ID[8:16]) return v0, v1 } diff --git a/middleware/torrentapproval/torrentapproval.go b/middleware/torrentapproval/torrentapproval.go index 8842e39..00e8928 100644 --- a/middleware/torrentapproval/torrentapproval.go +++ b/middleware/torrentapproval/torrentapproval.go @@ -28,7 +28,7 @@ func (d driver) NewHook(optionBytes []byte) (middleware.Hook, error) { var cfg Config err := yaml.Unmarshal(optionBytes, &cfg) if err != nil { - return nil, fmt.Errorf("invalid options for middleware %s: %s", Name, err) + return nil, fmt.Errorf("invalid options for middleware %s: %w", Name, err) } return NewHook(cfg) diff --git a/middleware/varinterval/varinterval.go b/middleware/varinterval/varinterval.go index 42b1adc..a4af097 100644 --- a/middleware/varinterval/varinterval.go +++ b/middleware/varinterval/varinterval.go @@ -29,7 +29,7 @@ func (d driver) NewHook(optionBytes []byte) (middleware.Hook, error) { var cfg Config err := yaml.Unmarshal(optionBytes, &cfg) if err != nil { - return nil, fmt.Errorf("invalid options for middleware %s: %s", Name, err) + return nil, fmt.Errorf("invalid options for middleware %s: %w", Name, err) } return NewHook(cfg) @@ -77,8 +77,7 @@ type hook struct { // NewHook creates a middleware to randomly modify the announce interval from // the given config. func NewHook(cfg Config) (middleware.Hook, error) { - err := checkConfig(cfg) - if err != nil { + if err := checkConfig(cfg); err != nil { return nil, err } @@ -96,12 +95,12 @@ func (h *hook) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceReque if h.cfg.ModifyResponseProbability == 1 || p < h.cfg.ModifyResponseProbability { // Generate the increase delta. v, _, _ = random.Intn(s0, s1, h.cfg.MaxIncreaseDelta) - addSeconds := time.Duration(v+1) * time.Second + deltaDuration := time.Duration(v+1) * time.Second - resp.Interval += addSeconds + resp.Interval += deltaDuration if h.cfg.ModifyMinInterval { - resp.MinInterval += addSeconds + resp.MinInterval += deltaDuration } return ctx, nil diff --git a/pkg/metrics/server.go b/pkg/metrics/server.go index 61aa455..4bf0e13 100644 --- a/pkg/metrics/server.go +++ b/pkg/metrics/server.go @@ -4,6 +4,7 @@ package metrics import ( "context" + "errors" "net/http" "net/http/pprof" @@ -49,7 +50,7 @@ func NewServer(addr string) *Server { } go func() { - if err := s.srv.ListenAndServe(); err != http.ErrServerClosed { + if err := s.srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { log.Fatal("failed while serving prometheus", log.Err(err)) } }() diff --git a/storage/redis/peer_store.go b/storage/redis/peer_store.go index fbcf54a..9c61f0f 100644 --- a/storage/redis/peer_store.go +++ b/storage/redis/peer_store.go @@ -25,6 +25,7 @@ package redis import ( "encoding/binary" + "errors" "net" "strconv" "sync" @@ -301,7 +302,7 @@ func (ps *peerStore) populateProm() { defer conn.Close() for _, group := range ps.groups() { - if n, err := redis.Int64(conn.Do("GET", ps.infohashCountKey(group))); err != nil && err != redis.ErrNil { + if n, err := redis.Int64(conn.Do("GET", ps.infohashCountKey(group))); err != nil && !errors.Is(err, redis.ErrNil) { log.Error("storage: GET counter failure", log.Fields{ "key": ps.infohashCountKey(group), "error": err, @@ -309,7 +310,7 @@ func (ps *peerStore) populateProm() { } else { numInfohashes += n } - if n, err := redis.Int64(conn.Do("GET", ps.seederCountKey(group))); err != nil && err != redis.ErrNil { + if n, err := redis.Int64(conn.Do("GET", ps.seederCountKey(group))); err != nil && !errors.Is(err, redis.ErrNil) { log.Error("storage: GET counter failure", log.Fields{ "key": ps.seederCountKey(group), "error": err, @@ -317,7 +318,7 @@ func (ps *peerStore) populateProm() { } else { numSeeders += n } - if n, err := redis.Int64(conn.Do("GET", ps.leecherCountKey(group))); err != nil && err != redis.ErrNil { + if n, err := redis.Int64(conn.Do("GET", ps.leecherCountKey(group))); err != nil && !errors.Is(err, redis.ErrNil) { log.Error("storage: GET counter failure", log.Fields{ "key": ps.leecherCountKey(group), "error": err, @@ -789,7 +790,7 @@ func (ps *peerStore) collectGarbage(cutoff time.Time) error { _ = conn.Send("DECR", ps.infohashCountKey(group)) } _, err = redis.Values(conn.Do("EXEC")) - if err != nil && err != redis.ErrNil { + if err != nil && !errors.Is(err, redis.ErrNil) { log.Error("storage: Redis EXEC failure", log.Fields{ "group": group, "infohash": ihStr, @@ -797,7 +798,7 @@ func (ps *peerStore) collectGarbage(cutoff time.Time) error { }) } } else { - if _, err = conn.Do("UNWATCH"); err != nil && err != redis.ErrNil { + if _, err = conn.Do("UNWATCH"); err != nil && !errors.Is(err, redis.ErrNil) { log.Error("storage: Redis UNWATCH failure", log.Fields{"error": err}) } } diff --git a/storage/redis/redis.go b/storage/redis/redis.go index fccf941..32ee287 100644 --- a/storage/redis/redis.go +++ b/storage/redis/redis.go @@ -69,7 +69,7 @@ func (rc *redisConnector) NewPool() *redis.Pool { }, // PINGs connections that have been idle more than 10 seconds TestOnBorrow: func(c redis.Conn, t time.Time) error { - if time.Since(t) < time.Duration(10*time.Second) { + if time.Since(t) < 10*time.Second { return nil } _, err := c.Do("PING")