frontend: update to use non-object sanization

This commit is contained in:
Jimmy Zelinskie 2017-10-17 22:02:45 -04:00
parent 66e12c6684
commit ca823e0e5f
6 changed files with 65 additions and 80 deletions

View file

@ -1,61 +0,0 @@
package bittorrent
import (
"net"
log "github.com/sirupsen/logrus"
)
// ErrInvalidIP indicates an invalid IP for an Announce.
var ErrInvalidIP = ClientError("invalid IP")
// RequestSanitizer is used to replace unreasonable values in requests parsed
// from a frontend into sane values.
type RequestSanitizer struct {
MaxNumWant uint32 `yaml:"max_numwant"`
DefaultNumWant uint32 `yaml:"default_numwant"`
MaxScrapeInfoHashes uint32 `yaml:"max_scrape_infohashes"`
}
// SanitizeAnnounce enforces a max and default NumWant and coerces the peer's
// IP address into the proper format.
func (rs *RequestSanitizer) SanitizeAnnounce(r *AnnounceRequest) error {
if !r.NumWantProvided {
r.NumWant = rs.DefaultNumWant
} else if r.NumWant > rs.MaxNumWant {
r.NumWant = rs.MaxNumWant
}
if ip := r.Peer.IP.To4(); ip != nil {
r.Peer.IP.IP = ip
r.Peer.IP.AddressFamily = IPv4
} else if len(r.Peer.IP.IP) == net.IPv6len { // implies r.Peer.IP.To4() == nil
r.Peer.IP.AddressFamily = IPv6
} else {
return ErrInvalidIP
}
log.Debug("sanitized announce", rs, r)
return nil
}
// SanitizeScrape enforces a max number of infohashes for a single scrape
// request.
func (rs *RequestSanitizer) SanitizeScrape(r *ScrapeRequest) error {
if len(r.InfoHashes) > int(rs.MaxScrapeInfoHashes) {
r.InfoHashes = r.InfoHashes[:rs.MaxScrapeInfoHashes]
}
log.Debug("sanitized scrape", rs, r)
return nil
}
// LogFields renders the request sanitizer's configuration as a set of loggable
// fields.
func (rs *RequestSanitizer) LogFields() log.Fields {
return log.Fields{
"maxNumWant": rs.MaxNumWant,
"defaultNumWant": rs.DefaultNumWant,
"maxScrapeInfohashes": rs.MaxScrapeInfoHashes,
}
}

48
bittorrent/sanitize.go Normal file
View file

@ -0,0 +1,48 @@
package bittorrent
import (
"net"
"github.com/chihaya/chihaya/pkg/log"
)
// ErrInvalidIP indicates an invalid IP for an Announce.
var ErrInvalidIP = ClientError("invalid IP")
// SanitizeAnnounce enforces a max and default NumWant and coerces the peer's
// IP address into the proper format.
func SanitizeAnnounce(r *AnnounceRequest, maxNumWant, defaultNumWant uint32) error {
if !r.NumWantProvided {
r.NumWant = defaultNumWant
} else if r.NumWant > maxNumWant {
r.NumWant = maxNumWant
}
if ip := r.Peer.IP.To4(); ip != nil {
r.Peer.IP.IP = ip
r.Peer.IP.AddressFamily = IPv4
} else if len(r.Peer.IP.IP) == net.IPv6len { // implies r.Peer.IP.To4() == nil
r.Peer.IP.AddressFamily = IPv6
} else {
return ErrInvalidIP
}
log.Debug("sanitized announce", r, log.Fields{
"maxNumWant": maxNumWant,
"defaultNumWant": defaultNumWant,
})
return nil
}
// SanitizeScrape enforces a max number of infohashes for a single scrape
// request.
func SanitizeScrape(r *ScrapeRequest, maxScrapeInfoHashes uint32) error {
if len(r.InfoHashes) > int(maxScrapeInfoHashes) {
r.InfoHashes = r.InfoHashes[:maxScrapeInfoHashes]
}
log.Debug("sanitized scrape", r, log.Fields{
"maxScrapeInfoHashes": maxScrapeInfoHashes,
})
return nil
}

View file

@ -21,9 +21,6 @@ func init() {
prometheus.MustRegister(promResponseDurationMilliseconds)
}
// ErrInvalidIP indicates an invalid IP.
var ErrInvalidIP = bittorrent.ClientError("invalid IP")
var promResponseDurationMilliseconds = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "chihaya_http_response_duration_milliseconds",
@ -84,7 +81,7 @@ func (cfg Config) LogFields() log.Fields {
"realIPHeader": cfg.RealIPHeader,
"maxNumWant": cfg.MaxNumWant,
"defaultNumWant": cfg.DefaultNumWant,
"maxScrapeInfohashes": cfg.MaxScrapeInfoHashes,
"maxScrapeInfoHashes": cfg.MaxScrapeInfoHashes,
}
}
@ -280,7 +277,7 @@ func (f *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou
req.AddressFamily = bittorrent.IPv6
} else {
log.Error("http: invalid IP: neither v4 nor v6", log.Fields{"RemoteAddr": r.RemoteAddr})
WriteError(w, ErrInvalidIP)
WriteError(w, bittorrent.ErrInvalidIP)
return
}
af = new(bittorrent.AddressFamily)

View file

@ -13,9 +13,11 @@ import (
// If RealIPHeader is not empty string, the value of the first HTTP Header with
// that name will be used.
type ParseOptions struct {
AllowIPSpoofing bool `yaml:"allowIPSpoofing"`
RealIPHeader string `yaml:"realIPHeader"`
bittorrent.RequestSanitizer `yaml:",inline"`
AllowIPSpoofing bool `yaml:"allow_ip_spoofing"`
RealIPHeader string `yaml:"real_ip_header"`
MaxNumWant uint32 `yaml:"max_numwant"`
DefaultNumWant uint32 `yaml:"default_numwant"`
MaxScrapeInfoHashes uint32 `yaml:"max_scrape_infohashes"`
}
// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request.
@ -102,7 +104,7 @@ func ParseAnnounce(r *http.Request, opts ParseOptions) (*bittorrent.AnnounceRequ
return nil, bittorrent.ClientError("failed to parse peer IP address")
}
if err := opts.SanitizeAnnounce(request); err != nil {
if err := bittorrent.SanitizeAnnounce(request, opts.MaxNumWant, opts.DefaultNumWant); err != nil {
return nil, err
}
@ -126,7 +128,7 @@ func ParseScrape(r *http.Request, opts ParseOptions) (*bittorrent.ScrapeRequest,
Params: qp,
}
if err := opts.SanitizeScrape(request); err != nil {
if err := bittorrent.SanitizeScrape(request, opts.MaxScrapeInfoHashes); err != nil {
return nil, err
}

View file

@ -26,9 +26,6 @@ func init() {
prometheus.MustRegister(promResponseDurationMilliseconds)
}
// ErrInvalidIP indicates an invalid IP.
var ErrInvalidIP = bittorrent.ClientError("invalid IP")
var promResponseDurationMilliseconds = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "chihaya_udp_response_duration_milliseconds",
@ -84,7 +81,7 @@ func (cfg Config) LogFields() log.Fields {
"allowIPSpoofing": cfg.AllowIPSpoofing,
"maxNumWant": cfg.MaxNumWant,
"defaultNumWant": cfg.DefaultNumWant,
"maxScrapeInfohashes": cfg.MaxScrapeInfoHashes,
"maxScrapeInfoHashes": cfg.MaxScrapeInfoHashes,
}
}
@ -317,7 +314,7 @@ func (t *Frontend) handleRequest(r Request, w ResponseWriter) (actionName string
req.AddressFamily = bittorrent.IPv6
} else {
log.Error("udp: invalid IP: neither v4 nor v6", log.Fields{"IP": r.IP})
WriteError(w, txID, ErrInvalidIP)
WriteError(w, txID, bittorrent.ErrInvalidIP)
return
}
af = new(bittorrent.AddressFamily)

View file

@ -49,8 +49,10 @@ var (
//
// If AllowIPSpoofing is true, IPs provided via params will be used.
type ParseOptions struct {
AllowIPSpoofing bool `yaml:"allowIPSpoofing"`
bittorrent.RequestSanitizer `yaml:",inline"`
AllowIPSpoofing bool `yaml:"allow_ip_spoofing"`
MaxNumWant uint32 `yaml:"max_numwant"`
DefaultNumWant uint32 `yaml:"default_numwant"`
MaxScrapeInfoHashes uint32 `yaml:"max_scrape_infohashes"`
}
// ParseAnnounce parses an AnnounceRequest from a UDP request.
@ -117,7 +119,7 @@ func ParseAnnounce(r Request, v6 bool, opts ParseOptions) (*bittorrent.AnnounceR
Params: params,
}
if err := opts.SanitizeAnnounce(request); err != nil {
if err := bittorrent.SanitizeAnnounce(request, opts.MaxNumWant, opts.DefaultNumWant); err != nil {
return nil, err
}
@ -208,7 +210,7 @@ func ParseScrape(r Request, opts ParseOptions) (*bittorrent.ScrapeRequest, error
// Sanitize the request.
request := &bittorrent.ScrapeRequest{InfoHashes: infohashes}
if err := opts.SanitizeScrape(request); err != nil {
if err := bittorrent.SanitizeScrape(request, opts.MaxScrapeInfoHashes); err != nil {
return nil, err
}