tracker/http/tracker.go

213 lines
5 KiB
Go
Raw Normal View History

2015-01-01 18:02:25 +01:00
// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.
package http
import (
"errors"
"net"
"net/http"
"strconv"
"github.com/julienschmidt/httprouter"
"github.com/chihaya/chihaya/config"
"github.com/chihaya/chihaya/http/query"
"github.com/chihaya/chihaya/tracker/models"
)
// newAnnounce parses an HTTP request and generates a models.Announce.
2015-02-20 08:06:44 +01:00
func (s *Server) newAnnounce(r *http.Request, p httprouter.Params) (*models.Announce, error) {
q, err := query.New(r.URL.RawQuery)
if err != nil {
return nil, err
}
compact := q.Params["compact"] != "0"
event, _ := q.Params["event"]
2015-02-20 08:06:44 +01:00
numWant := requestedPeerCount(q, s.config.NumWantFallback)
infohash, exists := q.Params["info_hash"]
if !exists {
return nil, models.ErrMalformedRequest
}
peerID, exists := q.Params["peer_id"]
if !exists {
return nil, models.ErrMalformedRequest
}
port, err := q.Uint64("port")
if err != nil {
return nil, models.ErrMalformedRequest
}
left, err := q.Uint64("left")
ipv4, ipv6, err := requestedEndpoint(q, r, &s.config.NetConfig)
if err != nil {
return nil, models.ErrMalformedRequest
}
var ipv4Endpoint, ipv6Endpoint models.Endpoint
if ipv4 != nil {
ipv4Endpoint = *ipv4
// If the port we couldn't get the port before, fallback to the port param.
if ipv4Endpoint.Port == uint16(0) {
ipv4Endpoint.Port = uint16(port)
}
}
if ipv6 != nil {
ipv6Endpoint = *ipv6
// If the port we couldn't get the port before, fallback to the port param.
if ipv6Endpoint.Port == uint16(0) {
ipv6Endpoint.Port = uint16(port)
}
}
if err != nil {
return nil, models.ErrMalformedRequest
}
downloaded, err := q.Uint64("downloaded")
if err != nil {
return nil, models.ErrMalformedRequest
}
uploaded, err := q.Uint64("uploaded")
if err != nil {
return nil, models.ErrMalformedRequest
}
return &models.Announce{
2015-02-20 08:06:44 +01:00
Config: s.config,
Compact: compact,
Downloaded: downloaded,
Event: event,
IPv4: ipv4Endpoint,
IPv6: ipv6Endpoint,
Infohash: infohash,
Left: left,
NumWant: numWant,
Passkey: p.ByName("passkey"),
PeerID: peerID,
Uploaded: uploaded,
}, nil
}
// newScrape parses an HTTP request and generates a models.Scrape.
2015-02-20 08:06:44 +01:00
func (s *Server) newScrape(r *http.Request, p httprouter.Params) (*models.Scrape, error) {
q, err := query.New(r.URL.RawQuery)
if err != nil {
return nil, err
}
if q.Infohashes == nil {
if _, exists := q.Params["info_hash"]; !exists {
// There aren't any infohashes.
return nil, models.ErrMalformedRequest
}
q.Infohashes = []string{q.Params["info_hash"]}
}
return &models.Scrape{
2015-02-20 08:06:44 +01:00
Config: s.config,
Passkey: p.ByName("passkey"),
Infohashes: q.Infohashes,
}, nil
}
// requestedPeerCount returns the wanted peer count or the provided fallback.
func requestedPeerCount(q *query.Query, fallback int) int {
if numWantStr, exists := q.Params["numwant"]; exists {
numWant, err := strconv.Atoi(numWantStr)
if err != nil {
return fallback
}
return numWant
}
return fallback
}
// requestedEndpoint returns the IP address and port pairs for a request. If
// there are multiple in the request, one IPv4 and one IPv6 will be returned.
func requestedEndpoint(q *query.Query, r *http.Request, cfg *config.NetConfig) (v4, v6 *models.Endpoint, err error) {
var done bool
if cfg.AllowIPSpoofing {
if str, ok := q.Params["ip"]; ok {
if v4, v6, done = getEndpoints(str, v4, v6, cfg); done {
return
}
}
if str, ok := q.Params["ipv4"]; ok {
if v4, v6, done = getEndpoints(str, v4, v6, cfg); done {
return
}
}
if str, ok := q.Params["ipv6"]; ok {
if v4, v6, done = getEndpoints(str, v4, v6, cfg); done {
return
}
}
}
if cfg.RealIPHeader != "" {
if xRealIPs, ok := r.Header[cfg.RealIPHeader]; ok {
if v4, v6, done = getEndpoints(string(xRealIPs[0]), v4, v6, cfg); done {
return
}
}
} else {
if r.RemoteAddr == "" && v4 == nil {
if v4, v6, done = getEndpoints("127.0.0.1", v4, v6, cfg); done {
return
}
}
if v4, v6, done = getEndpoints(r.RemoteAddr, v4, v6, cfg); done {
return
}
}
if v4 == nil && v6 == nil {
err = errors.New("failed to parse IP address")
}
return
}
func getEndpoints(ipstr string, ipv4, ipv6 *models.Endpoint, cfg *config.NetConfig) (*models.Endpoint, *models.Endpoint, bool) {
host, port, err := net.SplitHostPort(ipstr)
if err != nil {
host = ipstr
}
// We can ignore this error, because ports that are 0 are assumed to be the
// port parameter provided in the "port" param of the announce request.
parsedPort, _ := strconv.ParseUint(port, 10, 16)
if ip := net.ParseIP(host); ip != nil {
ipTo4 := ip.To4()
if ipv4 == nil && ipTo4 != nil {
ipv4 = &models.Endpoint{ipTo4, uint16(parsedPort)}
} else if ipv6 == nil && ipTo4 == nil {
ipv6 = &models.Endpoint{ip, uint16(parsedPort)}
}
}
var done bool
if cfg.DualStackedPeers {
done = ipv4 != nil && ipv6 != nil
} else {
done = ipv4 != nil || ipv6 != nil
}
return ipv4, ipv6, done
}