frontend/udp: add request sanitization

This commit is contained in:
Jimmy Zelinskie 2017-10-08 17:35:50 -04:00
parent 6dee48ce17
commit 47b5e67345
2 changed files with 46 additions and 23 deletions

View file

@ -70,8 +70,8 @@ type Config struct {
Addr string `yaml:"addr"` Addr string `yaml:"addr"`
PrivateKey string `yaml:"private_key"` PrivateKey string `yaml:"private_key"`
MaxClockSkew time.Duration `yaml:"max_clock_skew"` MaxClockSkew time.Duration `yaml:"max_clock_skew"`
AllowIPSpoofing bool `yaml:"allow_ip_spoofing"`
EnableRequestTiming bool `yaml:"enable_request_timing"` EnableRequestTiming bool `yaml:"enable_request_timing"`
ParseOptions `yaml:",inline"`
} }
// LogFields renders the current config as a set of Logrus fields. // LogFields renders the current config as a set of Logrus fields.
@ -80,8 +80,11 @@ func (cfg Config) LogFields() log.Fields {
"addr": cfg.Addr, "addr": cfg.Addr,
"privateKey": cfg.PrivateKey, "privateKey": cfg.PrivateKey,
"maxClockSkew": cfg.MaxClockSkew, "maxClockSkew": cfg.MaxClockSkew,
"allowIPSpoofing": cfg.AllowIPSpoofing,
"enableRequestTiming": cfg.EnableRequestTiming, "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" actionName = "announce"
var req *bittorrent.AnnounceRequest var req *bittorrent.AnnounceRequest
req, err = ParseAnnounce(r, t.AllowIPSpoofing, actionID == announceV6ActionID) req, err = ParseAnnounce(r, actionID == announceV6ActionID, t.ParseOptions)
if err != nil { if err != nil {
WriteError(w, txID, err) WriteError(w, txID, err)
return return
@ -302,7 +305,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (actionName string
actionName = "scrape" actionName = "scrape"
var req *bittorrent.ScrapeRequest var req *bittorrent.ScrapeRequest
req, err = ParseScrape(r) req, err = ParseScrape(r, t.ParseOptions)
if err != nil { if err != nil {
WriteError(w, txID, err) WriteError(w, txID, err)
return return

View file

@ -45,14 +45,19 @@ var (
errUnknownOptionType = bittorrent.ClientError("unknown option type") 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. // 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 is parsed the "opentracker way":
//
// 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/ // 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 ipEnd := 84 + net.IPv4len
if v6 { if v6 {
ipEnd = 84 + net.IPv6len ipEnd = 84 + net.IPv6len
@ -74,12 +79,14 @@ func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceReq
} }
ip := r.IP ip := r.IP
ipProvided := false
ipbytes := r.Packet[84:ipEnd] ipbytes := r.Packet[84:ipEnd]
if allowIPSpoofing { if opts.AllowIPSpoofing {
// Make sure the bytes are copied to a new slice. // Make sure the bytes are copied to a new slice.
copy(ip, net.IP(ipbytes)) 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. // We have no IP address to fallback on.
return nil, errMalformedIP return nil, errMalformedIP
} }
@ -92,20 +99,29 @@ func ParseAnnounce(r Request, allowIPSpoofing, v6 bool) (*bittorrent.AnnounceReq
return nil, err return nil, err
} }
return &bittorrent.AnnounceRequest{ request := &bittorrent.AnnounceRequest{
Event: eventIDs[eventID], Event: eventIDs[eventID],
InfoHash: bittorrent.InfoHashFromBytes(infohash), InfoHash: bittorrent.InfoHashFromBytes(infohash),
NumWant: uint32(numWant), NumWant: uint32(numWant),
Left: left, Left: left,
Downloaded: downloaded, Downloaded: downloaded,
Uploaded: uploaded, Uploaded: uploaded,
IPProvided: ipProvided,
NumWantProvided: true,
EventProvided: true,
Peer: bittorrent.Peer{ Peer: bittorrent.Peer{
ID: bittorrent.PeerIDFromBytes(peerID), ID: bittorrent.PeerIDFromBytes(peerID),
IP: bittorrent.IP{IP: ip}, IP: bittorrent.IP{IP: ip},
Port: port, Port: port,
}, },
Params: params, Params: params,
}, nil }
if err := opts.SanitizeAnnounce(request); err != nil {
return nil, err
}
return request, nil
} }
type buffer struct { type buffer struct {
@ -170,7 +186,7 @@ func handleOptionalParameters(packet []byte) (bittorrent.Params, error) {
} }
// ParseScrape parses a ScrapeRequest from a UDP request. // 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 a scrape isn't at least 36 bytes long, it's malformed.
if len(r.Packet) < 36 { if len(r.Packet) < 36 {
return nil, errMalformedPacket return nil, errMalformedPacket
@ -190,7 +206,11 @@ func ParseScrape(r Request) (*bittorrent.ScrapeRequest, error) {
r.Packet = r.Packet[20:] r.Packet = r.Packet[20:]
} }
return &bittorrent.ScrapeRequest{ // Sanitize the request.
InfoHashes: infohashes, request := &bittorrent.ScrapeRequest{InfoHashes: infohashes}
}, nil if err := opts.SanitizeScrape(request); err != nil {
return nil, err
}
return request, nil
} }