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"`
|
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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue