2014-07-17 06:54:53 +02:00
|
|
|
// Copyright 2014 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 (
|
2014-07-23 21:56:15 +02:00
|
|
|
"errors"
|
|
|
|
"net"
|
2014-07-17 06:54:53 +02:00
|
|
|
"net/http"
|
2014-07-23 21:56:15 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2014-07-17 06:54:53 +02:00
|
|
|
|
|
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
|
|
|
|
"github.com/chihaya/chihaya/config"
|
2014-07-17 07:00:26 +02:00
|
|
|
"github.com/chihaya/chihaya/http/query"
|
2014-07-17 06:54:53 +02:00
|
|
|
"github.com/chihaya/chihaya/tracker/models"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewAnnounce parses an HTTP request and generates a models.Announce.
|
|
|
|
func NewAnnounce(cfg *config.Config, 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"]
|
2014-07-23 21:56:15 +02:00
|
|
|
numWant := requestedPeerCount(q, cfg.NumWantFallback)
|
2014-07-17 06:54:53 +02:00
|
|
|
|
|
|
|
infohash, exists := q.Params["info_hash"]
|
|
|
|
if !exists {
|
|
|
|
return nil, models.ErrMalformedRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
peerID, exists := q.Params["peer_id"]
|
|
|
|
if !exists {
|
|
|
|
return nil, models.ErrMalformedRequest
|
|
|
|
}
|
|
|
|
|
2014-07-23 21:56:15 +02:00
|
|
|
ipv4, ipv6, err := requestedIP(q, r, &cfg.NetConfig)
|
2014-07-17 06:54:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, models.ErrMalformedRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
port, err := q.Uint64("port")
|
|
|
|
if err != nil {
|
|
|
|
return nil, models.ErrMalformedRequest
|
|
|
|
}
|
|
|
|
|
|
|
|
left, err := q.Uint64("left")
|
|
|
|
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{
|
|
|
|
Config: cfg,
|
|
|
|
Compact: compact,
|
|
|
|
Downloaded: downloaded,
|
|
|
|
Event: event,
|
2014-07-23 19:15:04 +02:00
|
|
|
IPv4: ipv4,
|
|
|
|
IPv6: ipv6,
|
2014-07-17 06:54:53 +02:00
|
|
|
Infohash: infohash,
|
|
|
|
Left: left,
|
|
|
|
NumWant: numWant,
|
|
|
|
Passkey: p.ByName("passkey"),
|
|
|
|
PeerID: peerID,
|
|
|
|
Port: port,
|
|
|
|
Uploaded: uploaded,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScrape parses an HTTP request and generates a models.Scrape.
|
|
|
|
func NewScrape(cfg *config.Config, 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{
|
|
|
|
Config: cfg,
|
|
|
|
|
|
|
|
Passkey: p.ByName("passkey"),
|
|
|
|
Infohashes: q.Infohashes,
|
|
|
|
}, nil
|
|
|
|
}
|
2014-07-23 21:56:15 +02:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// requestedIP returns the IP addresses for a request. If there are multiple
|
|
|
|
// IP addresses in the request, one IPv4 and one IPv6 will be returned.
|
|
|
|
func requestedIP(q *query.Query, r *http.Request, cfg *config.NetConfig) (v4, v6 net.IP, err error) {
|
|
|
|
var done bool
|
|
|
|
|
|
|
|
if cfg.AllowIPSpoofing {
|
|
|
|
if str, ok := q.Params["ip"]; ok {
|
|
|
|
if v4, v6, done = getIPs(str, v4, v6, cfg); done {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if str, ok := q.Params["ipv4"]; ok {
|
|
|
|
if v4, v6, done = getIPs(str, v4, v6, cfg); done {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if str, ok := q.Params["ipv6"]; ok {
|
|
|
|
if v4, v6, done = getIPs(str, v4, v6, cfg); done {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.RealIPHeader != "" {
|
|
|
|
if xRealIPs, ok := q.Params[cfg.RealIPHeader]; ok {
|
|
|
|
if v4, v6, done = getIPs(string(xRealIPs[0]), v4, v6, cfg); done {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if r.RemoteAddr == "" {
|
|
|
|
if v4 == nil {
|
|
|
|
v4 = net.ParseIP("127.0.0.1")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if idx := strings.LastIndex(r.RemoteAddr, ":"); idx != -1 {
|
|
|
|
if v4, v6, done = getIPs(r.RemoteAddr[0:idx], v4, v6, cfg); done {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v4 == nil && v6 == nil {
|
|
|
|
err = errors.New("failed to parse IP address")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getIPs(ipstr string, ipv4, ipv6 net.IP, cfg *config.NetConfig) (net.IP, net.IP, bool) {
|
|
|
|
var done bool
|
|
|
|
|
|
|
|
if ip := net.ParseIP(ipstr); ip != nil {
|
|
|
|
newIPv4 := ip.To4()
|
|
|
|
|
|
|
|
if ipv4 == nil && newIPv4 != nil {
|
|
|
|
ipv4 = newIPv4
|
|
|
|
} else if ipv6 == nil && newIPv4 == nil {
|
|
|
|
ipv6 = ip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.DualStackedPeers {
|
|
|
|
done = ipv4 != nil && ipv6 != nil
|
|
|
|
} else {
|
|
|
|
done = ipv4 != nil || ipv6 != nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return ipv4, ipv6, done
|
|
|
|
}
|