From 47b5e67345f6e271ce214e66d1843f411ed28127 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Sun, 8 Oct 2017 17:35:50 -0400 Subject: [PATCH] frontend/udp: add request sanitization --- frontend/udp/frontend.go | 11 +++++--- frontend/udp/parser.go | 58 +++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/frontend/udp/frontend.go b/frontend/udp/frontend.go index 4ecbf85..e50d182 100644 --- a/frontend/udp/frontend.go +++ b/frontend/udp/frontend.go @@ -70,8 +70,8 @@ type Config struct { Addr string `yaml:"addr"` PrivateKey string `yaml:"private_key"` MaxClockSkew time.Duration `yaml:"max_clock_skew"` - AllowIPSpoofing bool `yaml:"allow_ip_spoofing"` EnableRequestTiming bool `yaml:"enable_request_timing"` + ParseOptions `yaml:",inline"` } // LogFields renders the current config as a set of Logrus fields. @@ -80,8 +80,11 @@ func (cfg Config) LogFields() log.Fields { "addr": cfg.Addr, "privateKey": cfg.PrivateKey, "maxClockSkew": cfg.MaxClockSkew, - "allowIPSpoofing": cfg.AllowIPSpoofing, "enableRequestTiming": cfg.EnableRequestTiming, + "allowIPSpoofing": cfg.AllowIPSpoofing, + "maxNumWant": cfg.MaxNumWant, + "defaultNumWant": cfg.DefaultNumWant, + "maxScrapeInfohashes": cfg.MaxScrapeInfoHashes, } } @@ -278,7 +281,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (actionName string actionName = "announce" var req *bittorrent.AnnounceRequest - req, err = ParseAnnounce(r, t.AllowIPSpoofing, actionID == announceV6ActionID) + req, err = ParseAnnounce(r, actionID == announceV6ActionID, t.ParseOptions) if err != nil { WriteError(w, txID, err) return @@ -302,7 +305,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (actionName string actionName = "scrape" var req *bittorrent.ScrapeRequest - req, err = ParseScrape(r) + req, err = ParseScrape(r, t.ParseOptions) if err != nil { WriteError(w, txID, err) return diff --git a/frontend/udp/parser.go b/frontend/udp/parser.go index 9bc453c..e35e3b4 100644 --- a/frontend/udp/parser.go +++ b/frontend/udp/parser.go @@ -45,14 +45,19 @@ var ( errUnknownOptionType = bittorrent.ClientError("unknown option type") ) +// ParseOptions is the configuration used to parse an Announce Request. +// +// If AllowIPSpoofing is true, IPs provided via params will be used. +type ParseOptions struct { + AllowIPSpoofing bool `yaml:"allowIPSpoofing"` + bittorrent.RequestSanitizer `yaml:",inline"` +} + // 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 +// If v6 is true, the announce is parsed the "opentracker way": // http://opentracker.blog.h3q.com/2007/12/28/the-ipv6-situation/ -func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceRequest, error) { +func ParseAnnounce(r Request, v6 bool, opts ParseOptions) (*bittorrent.AnnounceRequest, error) { ipEnd := 84 + net.IPv4len if v6 { ipEnd = 84 + net.IPv6len @@ -74,12 +79,14 @@ func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceReq } ip := r.IP + ipProvided := false ipbytes := r.Packet[84:ipEnd] - if allowIPSpoofing { + if opts.AllowIPSpoofing { // Make sure the bytes are copied to a new slice. copy(ip, net.IP(ipbytes)) + ipProvided = true } - if !allowIPSpoofing && r.IP == nil { + if !opts.AllowIPSpoofing && r.IP == nil { // We have no IP address to fallback on. return nil, errMalformedIP } @@ -92,20 +99,29 @@ func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceReq return nil, err } - return &bittorrent.AnnounceRequest{ - Event: eventIDs[eventID], - InfoHash: bittorrent.InfoHashFromBytes(infohash), - NumWant: uint32(numWant), - Left: left, - Downloaded: downloaded, - Uploaded: uploaded, + request := &bittorrent.AnnounceRequest{ + Event: eventIDs[eventID], + InfoHash: bittorrent.InfoHashFromBytes(infohash), + NumWant: uint32(numWant), + Left: left, + Downloaded: downloaded, + Uploaded: uploaded, + IPProvided: ipProvided, + NumWantProvided: true, + EventProvided: true, Peer: bittorrent.Peer{ ID: bittorrent.PeerIDFromBytes(peerID), IP: bittorrent.IP{IP: ip}, Port: port, }, Params: params, - }, nil + } + + if err := opts.SanitizeAnnounce(request); err != nil { + return nil, err + } + + return request, nil } type buffer struct { @@ -170,7 +186,7 @@ func handleOptionalParameters(packet []byte) (bittorrent.Params, error) { } // ParseScrape parses a ScrapeRequest from a UDP request. -func ParseScrape(r Request) (*bittorrent.ScrapeRequest, error) { +func ParseScrape(r Request, opts ParseOptions) (*bittorrent.ScrapeRequest, error) { // If a scrape isn't at least 36 bytes long, it's malformed. if len(r.Packet) < 36 { return nil, errMalformedPacket @@ -190,7 +206,11 @@ func ParseScrape(r Request) (*bittorrent.ScrapeRequest, error) { r.Packet = r.Packet[20:] } - return &bittorrent.ScrapeRequest{ - InfoHashes: infohashes, - }, nil + // Sanitize the request. + request := &bittorrent.ScrapeRequest{InfoHashes: infohashes} + if err := opts.SanitizeScrape(request); err != nil { + return nil, err + } + + return request, nil }