frontend/http: add request sanitization
This commit is contained in:
parent
b7e6719129
commit
6dee48ce17
2 changed files with 58 additions and 33 deletions
|
@ -65,11 +65,10 @@ type Config struct {
|
|||
Addr string `yaml:"addr"`
|
||||
ReadTimeout time.Duration `yaml:"read_timeout"`
|
||||
WriteTimeout time.Duration `yaml:"write_timeout"`
|
||||
AllowIPSpoofing bool `yaml:"allow_ip_spoofing"`
|
||||
RealIPHeader string `yaml:"real_ip_header"`
|
||||
TLSCertPath string `yaml:"tls_cert_path"`
|
||||
TLSKeyPath string `yaml:"tls_key_path"`
|
||||
EnableRequestTiming bool `yaml:"enable_request_timing"`
|
||||
ParseOptions `yaml:",inline"`
|
||||
}
|
||||
|
||||
// LogFields renders the current config as a set of Logrus fields.
|
||||
|
@ -78,11 +77,14 @@ func (cfg Config) LogFields() log.Fields {
|
|||
"addr": cfg.Addr,
|
||||
"readTimeout": cfg.ReadTimeout,
|
||||
"writeTimeout": cfg.WriteTimeout,
|
||||
"allowIPSpoofing": cfg.AllowIPSpoofing,
|
||||
"realIPHeader": cfg.RealIPHeader,
|
||||
"tlsCertPath": cfg.TLSCertPath,
|
||||
"tlsKeyPath": cfg.TLSKeyPath,
|
||||
"enableRequestTiming": cfg.EnableRequestTiming,
|
||||
"allowIPSpoofing": cfg.AllowIPSpoofing,
|
||||
"realIPHeader": cfg.RealIPHeader,
|
||||
"maxNumWant": cfg.MaxNumWant,
|
||||
"defaultNumWant": cfg.DefaultNumWant,
|
||||
"maxScrapeInfohashes": cfg.MaxScrapeInfoHashes,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +221,7 @@ func (f *Frontend) announceRoute(w http.ResponseWriter, r *http.Request, _ httpr
|
|||
}
|
||||
}()
|
||||
|
||||
req, err := ParseAnnounce(r, f.RealIPHeader, f.AllowIPSpoofing)
|
||||
req, err := ParseAnnounce(r, f.ParseOptions)
|
||||
if err != nil {
|
||||
WriteError(w, err)
|
||||
return
|
||||
|
@ -258,7 +260,7 @@ func (f *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, _ httprou
|
|||
}
|
||||
}()
|
||||
|
||||
req, err := ParseScrape(r)
|
||||
req, err := ParseScrape(r, f.ParseOptions)
|
||||
if err != nil {
|
||||
WriteError(w, err)
|
||||
return
|
||||
|
|
|
@ -7,12 +7,19 @@ import (
|
|||
"github.com/chihaya/chihaya/bittorrent"
|
||||
)
|
||||
|
||||
// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request.
|
||||
// ParseOptions is the configuration used to parse an Announce Request.
|
||||
//
|
||||
// If allowIPSpoofing is true, IPs provided via params will be used.
|
||||
// If realIPHeader is not empty string, the first value of the HTTP Header with
|
||||
// If AllowIPSpoofing is true, IPs provided via BitTorrent params will be used.
|
||||
// If RealIPHeader is not empty string, the value of the first HTTP Header with
|
||||
// that name will be used.
|
||||
func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (*bittorrent.AnnounceRequest, error) {
|
||||
type ParseOptions struct {
|
||||
AllowIPSpoofing bool `yaml:"allowIPSpoofing"`
|
||||
RealIPHeader string `yaml:"realIPHeader"`
|
||||
bittorrent.RequestSanitizer `yaml:",inline"`
|
||||
}
|
||||
|
||||
// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request.
|
||||
func ParseAnnounce(r *http.Request, opts ParseOptions) (*bittorrent.AnnounceRequest, error) {
|
||||
qp, err := bittorrent.ParseURLData(r.RequestURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -20,15 +27,23 @@ func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (
|
|||
|
||||
request := &bittorrent.AnnounceRequest{Params: qp}
|
||||
|
||||
eventStr, _ := qp.String("event")
|
||||
request.Event, err = bittorrent.NewEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to provide valid client event")
|
||||
// Attempt to parse the event from the request.
|
||||
var eventStr string
|
||||
eventStr, request.EventProvided = qp.String("event")
|
||||
if request.EventProvided {
|
||||
request.Event, err = bittorrent.NewEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to provide valid client event")
|
||||
}
|
||||
} else {
|
||||
request.Event = bittorrent.None
|
||||
}
|
||||
|
||||
// Determine if the client expects a compact response.
|
||||
compactStr, _ := qp.String("compact")
|
||||
request.Compact = compactStr != "" && compactStr != "0"
|
||||
|
||||
// Parse the infohash from the request.
|
||||
infoHashes := qp.InfoHashes()
|
||||
if len(infoHashes) < 1 {
|
||||
return nil, bittorrent.ClientError("no info_hash parameter supplied")
|
||||
|
@ -38,6 +53,7 @@ func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (
|
|||
}
|
||||
request.InfoHash = infoHashes[0]
|
||||
|
||||
// Parse the PeerID from the request.
|
||||
peerID, ok := qp.String("peer_id")
|
||||
if !ok {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: peer_id")
|
||||
|
@ -47,43 +63,54 @@ func ParseAnnounce(r *http.Request, realIPHeader string, allowIPSpoofing bool) (
|
|||
}
|
||||
request.Peer.ID = bittorrent.PeerIDFromString(peerID)
|
||||
|
||||
// Determine the number of remaining pieces for the client.
|
||||
request.Left, err = qp.Uint64("left")
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: left")
|
||||
}
|
||||
|
||||
// Determine the number of pieces downloaded by the client.
|
||||
request.Downloaded, err = qp.Uint64("downloaded")
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: downloaded")
|
||||
}
|
||||
|
||||
// Determine the number of pieces shared by the client.
|
||||
request.Uploaded, err = qp.Uint64("uploaded")
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: uploaded")
|
||||
}
|
||||
|
||||
// Determine the number of peers the client wants in the response.
|
||||
numwant, err := qp.Uint64("numwant")
|
||||
if err != nil && err != bittorrent.ErrKeyNotFound {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: numwant")
|
||||
}
|
||||
request.NumWant = uint32(numwant)
|
||||
request.NumWantProvided = err == nil
|
||||
|
||||
// Parse the port where the client is listening.
|
||||
port, err := qp.Uint64("port")
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: port")
|
||||
}
|
||||
request.Peer.Port = uint16(port)
|
||||
|
||||
request.Peer.IP.IP = requestedIP(r, qp, realIPHeader, allowIPSpoofing)
|
||||
// Parse the IP address where the client is listening.
|
||||
request.Peer.IP.IP, request.IPProvided = requestedIP(r, qp, opts)
|
||||
if request.Peer.IP.IP == nil {
|
||||
return nil, bittorrent.ClientError("failed to parse peer IP address")
|
||||
}
|
||||
|
||||
if err := opts.SanitizeAnnounce(request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// ParseScrape parses an bittorrent.ScrapeRequest from an http.Request.
|
||||
func ParseScrape(r *http.Request) (*bittorrent.ScrapeRequest, error) {
|
||||
func ParseScrape(r *http.Request, opts ParseOptions) (*bittorrent.ScrapeRequest, error) {
|
||||
qp, err := bittorrent.ParseURLData(r.RequestURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -99,39 +126,35 @@ func ParseScrape(r *http.Request) (*bittorrent.ScrapeRequest, error) {
|
|||
Params: qp,
|
||||
}
|
||||
|
||||
if err := opts.SanitizeScrape(request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
// requestedIP determines the IP address for a BitTorrent client request.
|
||||
//
|
||||
// If allowIPSpoofing is true, IPs provided via params will be used.
|
||||
// If realIPHeader is not empty string, the first value of the HTTP Header with
|
||||
// that name will be used.
|
||||
func requestedIP(r *http.Request, p bittorrent.Params, realIPHeader string, allowIPSpoofing bool) net.IP {
|
||||
if allowIPSpoofing {
|
||||
func requestedIP(r *http.Request, p bittorrent.Params, opts ParseOptions) (ip net.IP, provided bool) {
|
||||
if opts.AllowIPSpoofing {
|
||||
if ipstr, ok := p.String("ip"); ok {
|
||||
ip := net.ParseIP(ipstr)
|
||||
return ip
|
||||
return net.ParseIP(ipstr), true
|
||||
}
|
||||
|
||||
if ipstr, ok := p.String("ipv4"); ok {
|
||||
ip := net.ParseIP(ipstr)
|
||||
return ip
|
||||
return net.ParseIP(ipstr), true
|
||||
}
|
||||
|
||||
if ipstr, ok := p.String("ipv6"); ok {
|
||||
ip := net.ParseIP(ipstr)
|
||||
return ip
|
||||
return net.ParseIP(ipstr), true
|
||||
}
|
||||
}
|
||||
|
||||
if realIPHeader != "" {
|
||||
if ips, ok := r.Header[realIPHeader]; ok && len(ips) > 0 {
|
||||
ip := net.ParseIP(ips[0])
|
||||
return ip
|
||||
if opts.RealIPHeader != "" {
|
||||
if ips, ok := r.Header[opts.RealIPHeader]; ok && len(ips) > 0 {
|
||||
return net.ParseIP(ips[0]), false
|
||||
}
|
||||
}
|
||||
|
||||
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
return net.ParseIP(host)
|
||||
return net.ParseIP(host), false
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue