Files
mochi/middleware/clientapproval/clientapproval.go
Lawrence, Rendall 61f859e3f6 (partially tested) simplify client approval m/w
* sanitize code
2023-03-23 00:34:10 +03:00

79 lines
2.3 KiB
Go

// Package clientapproval implements a Hook that fails an Announce based on a
// whitelist or blacklist of BitTorrent client IDs.
package clientapproval
import (
"context"
"errors"
"fmt"
"github.com/sot-tech/mochi/bittorrent"
"github.com/sot-tech/mochi/middleware"
"github.com/sot-tech/mochi/pkg/conf"
"github.com/sot-tech/mochi/storage"
)
// Name is the name by which this middleware is registered with Conf.
const Name = "client approval"
func init() {
middleware.RegisterBuilder(Name, build)
}
// ErrClientUnapproved is the error returned when a client's PeerID is invalid.
var ErrClientUnapproved = bittorrent.ClientError("client not allowed by mochi")
// Config represents all the values required by this middleware to validate
// peers based on their BitTorrent client ID.
type Config struct {
// Static list of client IDs.
ClientIDList []string `cfg:"client_id_list"`
// If Invert set to true, all client IDs stored in ClientIDList should be blacklisted.
Invert bool
}
type hook struct {
clientIDs map[ClientID]any
invert bool
}
func build(config conf.MapConfig, _ storage.PeerStorage) (middleware.Hook, error) {
var cfg Config
if err := config.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("middleware %s: %w", Name, err)
}
h := &hook{
clientIDs: make(map[ClientID]any, len(cfg.ClientIDList)),
invert: cfg.Invert,
}
for _, cidString := range cfg.ClientIDList {
cidBytes := []byte(cidString)
if len(cidBytes) != 6 {
return nil, errors.New("client ID " + cidString + " must be 6 bytes")
}
h.clientIDs[ClientID(cidBytes)] = true
}
return h, nil
}
// HandleAnnounce checks if specified ClientID is approved or not.
// If Config.Invert set to true and hash found in provided list, function will return ErrClientUnapproved,
// that means that ClientID is blacklisted.
func (h *hook) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequest, _ *bittorrent.AnnounceResponse) (context.Context, error) {
var err error
if _, contains := h.clientIDs[NewClientID(req.ID)]; contains == h.invert {
err = ErrClientUnapproved
}
return ctx, err
}
func (h *hook) HandleScrape(ctx context.Context, _ *bittorrent.ScrapeRequest, _ *bittorrent.ScrapeResponse) (context.Context, error) {
// Scrapes don't require any protection.
return ctx, nil
}