diff --git a/CONFIGURATION.md b/CONFIGURATION.md new file mode 100644 index 0000000..19fd4be --- /dev/null +++ b/CONFIGURATION.md @@ -0,0 +1,120 @@ +# Configuration + +Chihaya's behaviour is customized by setting up a JSON configuration file. +Available keys are as follows: + +##### `http_listen_addr` + + type: string + default: ":6881" + +The listen address for the HTTP server. If only a port is specified, the tracker will listen on all interfaces. + +##### `private_enabled` + + type: bool + default: true + +Whether this is a public or private tracker. + +##### `create_on_announce` + + type: bool + default: true + +Whether to register new torrents with the tracker when any client announces (`true`), or to return an error if the torrent doesn't exist (`false`). This should be set to `false` for private trackers in most cases. + +##### `purge_inactive_torrents` + + type: bool + default: true + +If torrents should be forgotten when there are no active peers. This should be set to `false` for private trackers. + +##### `announce` + + type: duration + default: "30m" + +The announce `interval` value sent to clients. This specifies how long clients should wait between regular announces. + +##### `min_announce` + + type: duration + default: "30m" + +The announce `min_interval` value sent to clients. This theoretically specifies the minimum allowed time between announces, but most clients don't really respect it. + +##### `default_num_want` + + type: integer + default: 50 + +The default maximum number of peers to return if the client has not requested a specific number. + +##### `allow_ip_spoofing` + + type: bool + default: true + +Whether peers are allowed to set their own IP via the various supported methods or if these are ignored. This must be enabled for dual-stack IP support, since there is no other way to determine both IPs of a peer otherwise. + +##### `dual_stacked_peers` + + type: bool + default: true + +True if peers may have both an IPv4 and IPv6 address, otherwise only one IP per peer will be used. + +##### `real_ip_header` + + type: string + default: blank + +An optional HTTP header indicating the upstream IP, for example `X-Forwarded-For` or `X-Real-IP`. Use this when running the tracker behind a reverse proxy. + +##### `respect_af` + + type: bool + default: false + +Whether responses should only include peers of the same address family as the announcing peer, or if peers of any family may be returned (i.e. both IPv4 and IPv6). + +##### `client_whitelist_enabled` + + type: bool + default: false + +Enables the peer ID whitelist. + +##### `client_whitelist` + + type: array of strings + default: [] + +List of peer ID prefixes to allow if `client_whitelist_enabled` is set to true. + +##### `freeleech_enabled` + + type: bool + default: false + +For private trackers only, whether download stats should be counted or ignored for users. + +##### `torrent_map_shards` + + type: integer + default: 1 + +Number of internal torrent maps to use. Leave this at 1 in general, however it can potentially improve performance when there are many unique torrents and few peers per torrent. + +- `http_request_timeout: "10s"` +- `http_read_timeout: "10s"` +- `http_write_timeout: "10s"` +- `http_listen_limit: 0` +- `driver: "noop"` +- `stats_buffer_size: 0` +- `include_mem_stats: true` +- `verbose_mem_stats: false` +- `mem_stats_interval: "5s"` + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2248835 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# vim: ft=dockerfile +FROM golang +MAINTAINER Jimmy Zelinskie + +# Add files +WORKDIR /go/src/github.com/chihaya/chihaya/ +RUN mkdir -p /go/src/github.com/chihaya/chihaya/ +ADD chihaya.go /go/src/github.com/chihaya/chihaya/ +ADD backend /go/src/github.com/chihaya/chihaya/backend +ADD cmd /go/src/github.com/chihaya/chihaya/cmd +ADD config /go/src/github.com/chihaya/chihaya/config +ADD http /go/src/github.com/chihaya/chihaya/http +ADD stats /go/src/github.com/chihaya/chihaya/stats +ADD tracker /go/src/github.com/chihaya/chihaya/tracker +ADD Godeps /go/src/github.com/chihaya/chihaya/Godeps + +# Install +RUN go get ./... +RUN go install + +# docker run -p 6881:6881 -v $PATH_TO_DIR_WITH_CONF_FILE:/config quay.io/jzelinskie/chihaya +VOLUME ["/config"] +EXPOSE 6881 +CMD ["chihaya", "-config=/config/config.json", "-logtostderr=true"] diff --git a/README.md b/README.md index 202494c..d10b2db 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# Chihaya [![Build Status](https://api.travis-ci.org/chihaya/chihaya.svg?branch=master)](https://travis-ci.org/chihaya/chihaya) +# Chihaya + +[![GoDoc](https://godoc.org/github.com/chihaya/chihaya?status.svg)](https://godoc.org/github.com/chihaya/chihaya) +[![Build Status](https://api.travis-ci.org/chihaya/chihaya.svg?branch=master)](https://travis-ci.org/chihaya/chihaya) +[![Docker Repository on Quay.io](https://quay.io/repository/jzelinskie/chihaya/status "Docker Repository on Quay.io")](https://quay.io/repository/jzelinskie/chihaya) Chihaya is a high-performance [BitTorrent tracker] written in the Go programming language. It is still heavily under development and the current @@ -32,7 +36,7 @@ use-cases). ## Building & Installing -Chihaya requires Go 1.4, [Godep], and a [Go environment] previously setup. +Chihaya requires 64-bit Go 1.4, [Godep], and a [Go environment] previously set up. [Godep]: https://github.com/tools/godep [Go environment]: https://golang.org/doc/code.html @@ -65,30 +69,5 @@ $ godep go test -v ./... -bench . Copy [`example_config.json`](https://github.com/chihaya/chihaya/blob/master/example_config.json) to your choice of location, and update the values as required. -The available keys and their default values are as follows: +An explanation of the available keys can be found in [CONFIGURATION.md](https://github.com/chihaya/chihaya/blob/master/CONFIGURATION.md). -- `private_enabled: false` – if this is a private tracker -- `freeleech_enabled: false` – for private trackers, whether download stats should be counted for users -- `purge_inactive_torrents: true` – if torrents should be forgotten after some time -- `announce: "30m"` – the announce "interval" value sent to clients -- `min_announce: "15m"` – the announce "min_interval" value sent to clients -- `default_num_want: 50` – the default number of peers to return if the client has not specified -- `torrent_map_shards: 1` – number of torrent maps to use (leave this at 1 in general) -- `allow_ip_spoofing: true` – if peers are allowed to set their own IP, this must be enabled for dual-stack IP support -- `dual_stacked_peers: true` – if peers may have both an IPv4 and IPv6 address, otherwise only one IP per peer will be used -- `real_ip_header: ""` – optionally an HTTP header where the upstream IP is stored, for example `X-Forwarded-For` or `X-Real-IP` -- `respect_af: false` – if responses should only include peers of the same address family as the announcing peer -- `client_whitelist_enabled: false` – if peer IDs should be matched against the whitelist -- `client_whitelist: []` – list of peer ID prefixes to allow -- `http_listen_addr: ""` – listen address for the HTTP server -- `http_request_timeout: "10s"` -- `http_read_timeout: "10s"` -- `http_write_timeout: "10s"` -- `http_listen_limit: 0` -- `udp_listen_addr: ""` – listen address for the UDP server -- `udp_read_buffer_size: undefined` – size of the UDP socket's kernel read buffer -- `driver: "noop"` -- `stats_buffer_size: 0` -- `include_mem_stats: true` -- `verbose_mem_stats: false` -- `mem_stats_interval: "5s"` diff --git a/config/config.go b/config/config.go index f9d3e5e..9740d1b 100644 --- a/config/config.go +++ b/config/config.go @@ -75,6 +75,7 @@ type WhitelistConfig struct { // TrackerConfig is the configuration for tracker functionality. type TrackerConfig struct { + CreateOnAnnounce bool `json:"create_on_announce"` PrivateEnabled bool `json:"private_enabled"` FreeleechEnabled bool `json:"freeleech_enabled"` PurgeInactiveTorrents bool `json:"purge_inactive_torrents"` @@ -114,6 +115,7 @@ type Config struct { // DefaultConfig is a configuration that can be used as a fallback value. var DefaultConfig = Config{ TrackerConfig: TrackerConfig{ + CreateOnAnnounce: true, PrivateEnabled: false, FreeleechEnabled: false, PurgeInactiveTorrents: true, @@ -134,14 +136,14 @@ var DefaultConfig = Config{ }, HTTPConfig: HTTPConfig{ - HTTPListenAddr: "", + HTTPListenAddr: ":6881", HTTPRequestTimeout: Duration{10 * time.Second}, HTTPReadTimeout: Duration{10 * time.Second}, HTTPWriteTimeout: Duration{10 * time.Second}, }, UDPConfig: UDPConfig{ - UDPListenAddr: "", + UDPListenAddr: ":6882", }, DriverConfig: DriverConfig{ diff --git a/example_config.json b/example_config.json index 61bb654..f7b102f 100644 --- a/example_config.json +++ b/example_config.json @@ -1,4 +1,5 @@ { + "create_on_announce": true, "private_enabled": false, "freeleech_enabled": false, "purge_inactive_torrents": true, diff --git a/tracker/announce.go b/tracker/announce.go index eed040c..6277028 100644 --- a/tracker/announce.go +++ b/tracker/announce.go @@ -27,7 +27,7 @@ func (tkr *Tracker) HandleAnnounce(ann *models.Announce, w Writer) (err error) { torrent, err := tkr.FindTorrent(ann.Infohash) - if err == models.ErrTorrentDNE && !tkr.Config.PrivateEnabled { + if err == models.ErrTorrentDNE && tkr.Config.CreateOnAnnounce { torrent = &models.Torrent{ Infohash: ann.Infohash, Seeders: models.NewPeerMap(true, tkr.Config), @@ -231,20 +231,13 @@ func (tkr *Tracker) handlePeerEvent(ann *models.Announce, p *models.Peer) (snatc } case ann.Event == "completed": - v4seed := t.Seeders.Contains(p.Key()) - v6seed := t.Seeders.Contains(p.Key()) - if t.Leechers.Contains(p.Key()) { err = tkr.leecherFinished(t, p) } else { err = models.ErrBadRequest } - // If one of the dual-stacked peers is already a seeder, they have - // already snatched. - if !(v4seed || v6seed) { - snatched = true - } + snatched = true case t.Leechers.Contains(p.Key()) && ann.Left == 0: // A leecher completed but the event was never received. diff --git a/tracker/models/models.go b/tracker/models/models.go index 7ff5cd4..d10014e 100644 --- a/tracker/models/models.go +++ b/tracker/models/models.go @@ -8,6 +8,7 @@ package models import ( "net" + "strconv" "strings" "time" @@ -52,13 +53,18 @@ func IsPublicError(err error) bool { return cl || nf || pc } +// PeerList represents a list of peers: either seeders or leechers. type PeerList []Peer + +// PeerKey is the key used to uniquely identify a peer in a swarm. type PeerKey string -func NewPeerKey(peerID string, ip net.IP) PeerKey { - return PeerKey(peerID + "//" + ip.String()) +// NewPeerKey creates a properly formatted PeerKey. +func NewPeerKey(peerID string, ip net.IP, port string) PeerKey { + return PeerKey(peerID + "//" + ip.String() + ":" + port) } +// IP parses and returns the IP address for a given PeerKey. func (pk PeerKey) IP() net.IP { ip := net.ParseIP(strings.Split(string(pk), "//")[1]) if rval := ip.To4(); rval != nil { @@ -67,10 +73,16 @@ func (pk PeerKey) IP() net.IP { return ip } +// PeerID returns the PeerID section of a PeerKey. func (pk PeerKey) PeerID() string { return strings.Split(string(pk), "//")[0] } +// Port returns the port section of the PeerKey. +func (pk PeerKey) Port() string { + return strings.Split(string(pk), "//")[2] +} + // Peer is a participant in a swarm. type Peer struct { ID string `json:"id"` @@ -88,16 +100,21 @@ type Peer struct { LastAnnounce int64 `json:"last_announce"` } +// HasIPv4 determines if a peer's IP address can be represented as an IPv4 +// address. func (p *Peer) HasIPv4() bool { return !p.HasIPv6() } +// HasIPv6 determines if a peer's IP address can be represented as an IPv6 +// address. func (p *Peer) HasIPv6() bool { return len(p.IP) == net.IPv6len } +// Key returns a PeerKey for the given peer. func (p *Peer) Key() PeerKey { - return NewPeerKey(p.ID, p.IP) + return NewPeerKey(p.ID, p.IP, strconv.FormatUint(p.Port, 10)) } // Torrent is a swarm for a given torrent file.