frontend/udp: add request sanitization
This commit is contained in:
parent
6dee48ce17
commit
47b5e67345
2 changed files with 46 additions and 23 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue