This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

362
vendor/github.com/anacrolix/dht/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,362 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

25
vendor/github.com/anacrolix/dht/README.md generated vendored Normal file
View File

@@ -0,0 +1,25 @@
# dht
[![CircleCI](https://circleci.com/gh/anacrolix/dht.svg?style=shield)](https://circleci.com/gh/anacrolix/dht)
[![GoDoc](https://godoc.org/github.com/anacrolix/dht?status.svg)](https://godoc.org/github.com/anacrolix/dht)
[![Join the chat at https://gitter.im/anacrolix/torrent](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/anacrolix/torrent?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Installation
Install the library package with `go get github.com/anacrolix/dht`, or the provided cmds with `go get github.com/anacrolix/dht/cmd/...`.
## Commands
Here I'll describe what some of the provided commands in `./cmd` do.
Note that the [`godo`](https://github.com/anacrolix/godo) command which is invoked in the following examples builds and executes a Go import path, like `go run`. It's easier to use this convention than to spell out the install/invoke cycle for every single example.
### dht-ping
Pings DHT nodes with the given network addresses.
$ godo ./cmd/dht-ping router.bittorrent.com:6881 router.utorrent.com:6881
2015/04/01 17:21:23 main.go:33: dht server on [::]:60058
32f54e697351ff4aec29cdbaabf2fbe3467cc267 (router.bittorrent.com:6881): 648.218621ms
ebff36697351ff4aec29cdbaabf2fbe3467cc267 (router.utorrent.com:6881): 873.864706ms
2/2 responses (100.000000%)

45
vendor/github.com/anacrolix/dht/addr.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package dht
import (
"net"
"github.com/anacrolix/dht/krpc"
)
// Used internally to refer to node network addresses. String() is called a
// lot, and so can be optimized. Network() is not exposed, so that the
// interface does not satisfy net.Addr, as the underlying type must be passed
// to any OS-level function that take net.Addr.
type Addr interface {
UDPAddr() *net.UDPAddr
String() string
KRPC() krpc.NodeAddr
}
// Speeds up some of the commonly called Addr methods.
type cachedAddr struct {
ua net.UDPAddr
s string
}
func (ca cachedAddr) String() string {
return ca.s
}
func (ca cachedAddr) UDPAddr() *net.UDPAddr {
return &ca.ua
}
func (ca cachedAddr) KRPC() krpc.NodeAddr {
return krpc.NodeAddr{
IP: ca.ua.IP,
Port: ca.ua.Port,
}
}
func NewAddr(ua *net.UDPAddr) Addr {
return cachedAddr{
ua: *ua,
s: ua.String(),
}
}

233
vendor/github.com/anacrolix/dht/announce.go generated vendored Normal file
View File

@@ -0,0 +1,233 @@
package dht
// get_peers and announce_peers.
import (
"time"
"github.com/anacrolix/sync"
"github.com/anacrolix/torrent/logonce"
"github.com/willf/bloom"
"github.com/anacrolix/dht/krpc"
)
// Maintains state for an ongoing Announce operation. An Announce is started
// by calling Server.Announce.
type Announce struct {
mu sync.Mutex
Peers chan PeersValues
// Inner chan is set to nil when on close.
values chan PeersValues
stop chan struct{}
triedAddrs *bloom.BloomFilter
// True when contact with all starting addrs has been initiated. This
// prevents a race where the first transaction finishes before the rest
// have been opened, sees no other transactions are pending and ends the
// announce.
contactedStartAddrs bool
// How many transactions are still ongoing.
pending int
server *Server
infoHash int160
// Count of (probably) distinct addresses we've sent get_peers requests
// to.
numContacted int
// The torrent port that we're announcing.
announcePort int
// The torrent port should be determined by the receiver in case we're
// being NATed.
announcePortImplied bool
}
// Returns the number of distinct remote addresses the announce has queried.
func (a *Announce) NumContacted() int {
a.mu.Lock()
defer a.mu.Unlock()
return a.numContacted
}
func newBloomFilterForTraversal() *bloom.BloomFilter {
return bloom.NewWithEstimates(10000, 0.5)
}
// This is kind of the main thing you want to do with DHT. It traverses the
// graph toward nodes that store peers for the infohash, streaming them to the
// caller, and announcing the local node to each node if allowed and
// specified.
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool) (*Announce, error) {
startAddrs, err := s.traversalStartingAddrs()
if err != nil {
return nil, err
}
disc := &Announce{
Peers: make(chan PeersValues, 100),
stop: make(chan struct{}),
values: make(chan PeersValues),
triedAddrs: newBloomFilterForTraversal(),
server: s,
infoHash: int160FromByteArray(infoHash),
announcePort: port,
announcePortImplied: impliedPort,
}
// Function ferries from values to Values until discovery is halted.
go func() {
defer close(disc.Peers)
for {
select {
case psv := <-disc.values:
select {
case disc.Peers <- psv:
case <-disc.stop:
return
}
case <-disc.stop:
return
}
}
}()
go func() {
disc.mu.Lock()
defer disc.mu.Unlock()
for i, addr := range startAddrs {
if i != 0 {
disc.mu.Unlock()
time.Sleep(time.Millisecond)
disc.mu.Lock()
}
disc.contact(addr)
}
disc.contactedStartAddrs = true
// If we failed to contact any of the starting addrs, no transactions
// will complete triggering a check that there are no pending
// responses.
disc.maybeClose()
}()
return disc, nil
}
func validNodeAddr(addr Addr) bool {
ua := addr.UDPAddr()
if ua.Port == 0 {
return false
}
if ip4 := ua.IP.To4(); ip4 != nil && ip4[0] == 0 {
return false
}
return true
}
// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) gotNodeAddr(addr Addr) {
if !validNodeAddr(addr) {
return
}
if a.triedAddrs.Test([]byte(addr.String())) {
return
}
if a.server.ipBlocked(addr.UDPAddr().IP) {
return
}
a.contact(addr)
}
// TODO: Merge this with maybeGetPeersFromAddr.
func (a *Announce) contact(addr Addr) {
a.numContacted++
a.triedAddrs.Add([]byte(addr.String()))
a.pending++
go func() {
err := a.getPeers(addr)
if err == nil {
return
}
a.mu.Lock()
a.transactionClosed()
a.mu.Unlock()
}()
}
func (a *Announce) maybeClose() {
if a.contactedStartAddrs && a.pending == 0 {
a.close()
}
}
func (a *Announce) transactionClosed() {
a.pending--
a.maybeClose()
}
func (a *Announce) responseNode(node krpc.NodeInfo) {
a.gotNodeAddr(NewAddr(node.Addr.UDP()))
}
// Announce to a peer, if appropriate.
func (a *Announce) maybeAnnouncePeer(to Addr, token string, peerId *krpc.ID) {
if !a.server.config.NoSecurity && (peerId == nil || !NodeIdSecure(*peerId, to.UDPAddr().IP)) {
return
}
a.server.mu.Lock()
defer a.server.mu.Unlock()
err := a.server.announcePeer(to, a.infoHash, a.announcePort, token, a.announcePortImplied, nil)
if err != nil {
logonce.Stderr.Printf("error announcing peer: %s", err)
}
}
func (a *Announce) getPeers(addr Addr) error {
a.server.mu.Lock()
defer a.server.mu.Unlock()
return a.server.getPeers(addr, a.infoHash, func(m krpc.Msg, err error) {
// Register suggested nodes closer to the target info-hash.
if m.R != nil && m.SenderID() != nil {
expvars.Add("announce get_peers response nodes values", int64(len(m.R.Nodes)))
expvars.Add("announce get_peers response nodes6 values", int64(len(m.R.Nodes6)))
a.mu.Lock()
for _, n := range m.R.Nodes {
a.responseNode(n)
}
for _, n := range m.R.Nodes6 {
a.responseNode(n)
}
a.mu.Unlock()
select {
case a.values <- PeersValues{
Peers: m.R.Values,
NodeInfo: krpc.NodeInfo{
Addr: addr.KRPC(),
ID: *m.SenderID(),
},
}:
case <-a.stop:
}
a.maybeAnnouncePeer(addr, m.R.Token, m.SenderID())
}
a.mu.Lock()
a.transactionClosed()
a.mu.Unlock()
})
}
// Corresponds to the "values" key in a get_peers KRPC response. A list of
// peers that a node has reported as being in the swarm for a queried info
// hash.
type PeersValues struct {
Peers []Peer // Peers given in get_peers response.
krpc.NodeInfo // The node that gave the response.
}
// Stop the announce.
func (a *Announce) Close() {
a.mu.Lock()
defer a.mu.Unlock()
a.close()
}
func (a *Announce) close() {
select {
case <-a.stop:
default:
close(a.stop)
}
}

37
vendor/github.com/anacrolix/dht/bucket.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
package dht
type bucket struct {
nodes map[*node]struct{}
}
func (b *bucket) Len() int {
return len(b.nodes)
}
func (b *bucket) EachNode(f func(*node) bool) bool {
for n := range b.nodes {
if !f(n) {
return false
}
}
return true
}
func (b *bucket) AddNode(n *node, k int) {
if _, ok := b.nodes[n]; ok {
return
}
if b.nodes == nil {
b.nodes = make(map[*node]struct{}, k)
}
b.nodes[n] = struct{}{}
}
func (b *bucket) GetNode(addr Addr, id int160) *node {
for n := range b.nodes {
if n.hasAddrAndID(addr, id) {
return n
}
}
return nil
}

131
vendor/github.com/anacrolix/dht/dht.go generated vendored Normal file
View File

@@ -0,0 +1,131 @@
package dht
import (
"crypto"
crand "crypto/rand"
_ "crypto/sha1"
"errors"
"log"
"math/rand"
"net"
"time"
"github.com/anacrolix/missinggo"
"github.com/anacrolix/torrent/iplist"
"github.com/anacrolix/torrent/metainfo"
"github.com/anacrolix/dht/krpc"
)
func defaultQueryResendDelay() time.Duration {
return jitterDuration(5*time.Second, time.Second)
}
// Uniquely identifies a transaction to us.
type transactionKey struct {
RemoteAddr string // host:port
T string // The KRPC transaction ID.
}
type StartingNodesGetter func() ([]Addr, error)
// ServerConfig allows to set up a configuration of the `Server` instance
// to be created with NewServer
type ServerConfig struct {
// Set NodeId Manually. Caller must ensure that if NodeId does not conform
// to DHT Security Extensions, that NoSecurity is also set.
NodeId [20]byte
Conn net.PacketConn
// Don't respond to queries from other nodes.
Passive bool
StartingNodes StartingNodesGetter
// Disable the DHT security extension:
// http://www.libtorrent.org/dht_sec.html.
NoSecurity bool
// Initial IP blocklist to use. Applied before serving and bootstrapping
// begins.
IPBlocklist iplist.Ranger
// Used to secure the server's ID. Defaults to the Conn's LocalAddr(). Set
// to the IP that remote nodes will see, as that IP is what they'll use to
// validate our ID.
PublicIP net.IP
// Hook received queries. Return false if you don't want to propagate to
// the default handlers.
OnQuery func(query *krpc.Msg, source net.Addr) (propagate bool)
// Called when a peer successfully announces to us.
OnAnnouncePeer func(infoHash metainfo.Hash, peer Peer)
// How long to wait before resending queries that haven't received a
// response. Defaults to a random value between 4.5 and 5.5s.
QueryResendDelay func() time.Duration
// TODO: Expose Peers, to return NodeInfo for received get_peers queries.
}
// ServerStats instance is returned by Server.Stats() and stores Server metrics
type ServerStats struct {
// Count of nodes in the node table that responded to our last query or
// haven't yet been queried.
GoodNodes int
// Count of nodes in the node table.
Nodes int
// Transactions awaiting a response.
OutstandingTransactions int
// Individual announce_peer requests that got a success response.
SuccessfulOutboundAnnouncePeerQueries int64
// Nodes that have been blocked.
BadNodes uint
OutboundQueriesAttempted int64
}
func jitterDuration(average time.Duration, plusMinus time.Duration) time.Duration {
return average - plusMinus/2 + time.Duration(rand.Int63n(int64(plusMinus)))
}
type Peer = krpc.NodeAddr
func GlobalBootstrapAddrs() (addrs []Addr, err error) {
for _, s := range []string{
"router.utorrent.com:6881",
"router.bittorrent.com:6881",
"dht.transmissionbt.com:6881",
"dht.aelitis.com:6881", // Vuze
"router.silotis.us:6881", // IPv6
"dht.libtorrent.org:25401", // @arvidn's
} {
host, port, err := net.SplitHostPort(s)
if err != nil {
panic(err)
}
hostAddrs, err := net.LookupHost(host)
if err != nil {
log.Printf("error looking up %q: %v", s, err)
continue
}
for _, a := range hostAddrs {
ua, err := net.ResolveUDPAddr("udp", net.JoinHostPort(a, port))
if err != nil {
log.Printf("error resolving %q: %v", a, err)
continue
}
addrs = append(addrs, NewAddr(ua))
}
}
if len(addrs) == 0 {
err = errors.New("nothing resolved")
}
return
}
func RandomNodeID() (id [20]byte) {
crand.Read(id[:])
return
}
func MakeDeterministicNodeID(public net.Addr) (id [20]byte) {
h := crypto.SHA1.New()
h.Write([]byte(public.String()))
h.Sum(id[:0:20])
SecureNodeId(&id, missinggo.AddrIP(public))
return
}

22
vendor/github.com/anacrolix/dht/doc.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
// Package dht implements a Distributed Hash Table (DHT) part of
// the BitTorrent protocol,
// as specified by BEP 5: http://www.bittorrent.org/beps/bep_0005.html
//
// BitTorrent uses a "distributed hash table" (DHT)
// for storing peer contact information for "trackerless" torrents.
// In effect, each peer becomes a tracker.
// The protocol is based on Kademila DHT protocol and is implemented over UDP.
//
// Please note the terminology used to avoid confusion.
// A "peer" is a client/server listening on a TCP port that
// implements the BitTorrent protocol.
// A "node" is a client/server listening on a UDP port implementing
// the distributed hash table protocol.
// The DHT is composed of nodes and stores the location of peers.
// BitTorrent clients include a DHT node, which is used to contact other nodes
// in the DHT to get the location of peers to
// download from using the BitTorrent protocol.
//
// Standard use involves creating a Server, and calling Announce on it with
// the details of your local torrent client and infohash of interest.
package dht

19
vendor/github.com/anacrolix/dht/expvar.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package dht
import (
"expvar"
)
var (
read = expvar.NewInt("dhtRead")
readZeroPort = expvar.NewInt("dhtReadZeroPort")
readBlocked = expvar.NewInt("dhtReadBlocked")
readNotKRPCDict = expvar.NewInt("dhtReadNotKRPCDict")
readUnmarshalError = expvar.NewInt("dhtReadUnmarshalError")
readQuery = expvar.NewInt("dhtReadQuery")
readAnnouncePeer = expvar.NewInt("dhtReadAnnouncePeer")
announceErrors = expvar.NewInt("dhtAnnounceErrors")
writeErrors = expvar.NewInt("dhtWriteErrors")
writes = expvar.NewInt("dhtWrites")
expvars = expvar.NewMap("dht")
)

16
vendor/github.com/anacrolix/dht/go.mod generated vendored Normal file
View File

@@ -0,0 +1,16 @@
module github.com/anacrolix/dht
require (
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa
github.com/anacrolix/missinggo v0.0.0-20180621131740-7fc7cfea16ea
github.com/anacrolix/sync v0.0.0-20180611022320-3c4cb11f5a01
github.com/anacrolix/tagflag v0.0.0-20180605133421-f477c8c2f14c
github.com/anacrolix/torrent v0.0.0-20180622074351-fefeef4ee9eb
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2
github.com/davecgh/go-spew v1.1.0
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/testify v1.2.1
github.com/willf/bloom v0.0.0-20170505221640-54e3b963ee16
)

56
vendor/github.com/anacrolix/dht/go.sum generated vendored Normal file
View File

@@ -0,0 +1,56 @@
bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
github.com/anacrolix/dht v0.0.0-20180412060941-24cbf25b72a4/go.mod h1:hQfX2BrtuQsLQMYQwsypFAab/GvHg8qxwVi4OJdR1WI=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa h1:xCaATLKmn39QqLs3tUZYr6eKvezJV+FYvVOLTklxK6U=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/go-libutp v0.0.0-20180522111405-6baeb806518d/go.mod h1:beQSaSxwH2d9Eeu5ijrEnHei5Qhk+J6cDm1QkWFru4E=
github.com/anacrolix/log v0.0.0-20180412014343-2323884b361d/go.mod h1:sf/7c2aTldL6sRQj/4UKyjgVZBu2+M2z9wf7MmwPiew=
github.com/anacrolix/missinggo v0.0.0-20180522035225-b4a5853e62ff/go.mod h1:b0p+7cn+rWMIphK1gDH2hrDuwGOcbB6V4VXeSsEfHVk=
github.com/anacrolix/missinggo v0.0.0-20180621131740-7fc7cfea16ea h1:zgemcMeWIWXhwvxFSqVZzW695Q0erYNkfM1X3lZf/8w=
github.com/anacrolix/missinggo v0.0.0-20180621131740-7fc7cfea16ea/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
github.com/anacrolix/mmsg v0.0.0-20180515031531-a4a3ba1fc8bb/go.mod h1:x2/ErsYUmT77kezS63+wzZp8E3byYB0gzirM/WMBLfw=
github.com/anacrolix/sync v0.0.0-20171108081538-eee974e4f8c1/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w=
github.com/anacrolix/sync v0.0.0-20180611022320-3c4cb11f5a01 h1:14t4kCoWXaUXrHErRD0bLMNolOE50nyPA0gO8+J3hP8=
github.com/anacrolix/sync v0.0.0-20180611022320-3c4cb11f5a01/go.mod h1:+u91KiUuf0lyILI6x3n/XrW7iFROCZCG+TjgK8nW52w=
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/tagflag v0.0.0-20180605133421-f477c8c2f14c h1:kBea2eTysvA8qHScuAufErjwV6/wwqoOe03h7W9d1h0=
github.com/anacrolix/tagflag v0.0.0-20180605133421-f477c8c2f14c/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
github.com/anacrolix/torrent v0.0.0-20180622074351-fefeef4ee9eb h1:XTz2SGqfyyk/fvDYPZe3VaZqAH4heDTMz5WQmBm6F5w=
github.com/anacrolix/torrent v0.0.0-20180622074351-fefeef4ee9eb/go.mod h1:3vcFVxgOASslNXHdivT8spyMRBanMCenHRpe0u5vpBs=
github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2 h1:1B/+1BcRhOMG1KH/YhNIU8OppSWk5d/NGyfRla88CuY=
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e h1:Fw7ZmgiklsLh5EQWyHh1sumKSCG1+yjEctIpGKib87s=
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8=
github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=
github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/willf/bitset v1.1.3 h1:ekJIKh6+YbUIVt9DfNbkR5d6aFcFTLDRyJNAACURBg8=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bloom v0.0.0-20170505221640-54e3b963ee16 h1:hDGatoumfYOxzIZGsSylJuxTzu9k86BJl8OQhm72anI=
github.com/willf/bloom v0.0.0-20170505221640-54e3b963ee16/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

87
vendor/github.com/anacrolix/dht/int160.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
package dht
import (
"math"
"math/big"
)
type int160 struct {
bits [20]uint8
}
func (me *int160) AsByteArray() [20]byte {
return me.bits
}
func (me *int160) ByteString() string {
return string(me.bits[:])
}
func (me *int160) BitLen() int {
var a big.Int
a.SetBytes(me.bits[:])
return a.BitLen()
}
func (me *int160) SetBytes(b []byte) {
n := copy(me.bits[:], b)
if n != 20 {
panic(n)
}
}
func (me int160) Bytes() []byte {
return me.bits[:]
}
func (l int160) Cmp(r int160) int {
for i := range l.bits {
if l.bits[i] < r.bits[i] {
return -1
} else if l.bits[i] > r.bits[i] {
return 1
}
}
return 0
}
func (me *int160) SetMax() {
for i := range me.bits {
me.bits[i] = math.MaxUint8
}
}
func (me *int160) Xor(a, b *int160) {
for i := range me.bits {
me.bits[i] = a.bits[i] ^ b.bits[i]
}
}
func (me *int160) IsZero() bool {
for _, b := range me.bits {
if b != 0 {
return false
}
}
return true
}
func int160FromBytes(b []byte) (ret int160) {
ret.SetBytes(b)
return
}
func int160FromByteArray(b [20]byte) (ret int160) {
ret.SetBytes(b[:])
return
}
func int160FromByteString(s string) (ret int160) {
ret.SetBytes([]byte(s))
return
}
func distance(a, b *int160) (ret int160) {
ret.Xor(a, b)
return
}

View File

@@ -0,0 +1,25 @@
package krpc
type CompactIPv4NodeAddrs []NodeAddr
func (CompactIPv4NodeAddrs) ElemSize() int { return 6 }
func (me CompactIPv4NodeAddrs) MarshalBinary() ([]byte, error) {
return marshalBinarySlice(me)
}
func (me CompactIPv4NodeAddrs) MarshalBencode() ([]byte, error) {
return bencodeBytesResult(me.MarshalBinary())
}
func (me *CompactIPv4NodeAddrs) UnmarshalBinary(b []byte) error {
return unmarshalBinarySlice(me, b)
}
func (me *CompactIPv4NodeAddrs) UnmarshalBencode(b []byte) error {
return unmarshalBencodedBinary(me, b)
}
func (me CompactIPv4NodeAddrs) NodeAddrs() []NodeAddr {
return me
}

View File

@@ -0,0 +1,37 @@
package krpc
import "github.com/anacrolix/missinggo/slices"
type (
CompactIPv4NodeInfo []NodeInfo
)
func (CompactIPv4NodeInfo) ElemSize() int {
return 26
}
// func (me *CompactIPv4NodeInfo) Scrub() {
// slices.FilterInPlace(me, func(ni *NodeInfo) bool {
// ni.Addr.IP = ni.Addr.IP.To4()
// return ni.Addr.IP != nil
// })
// }
func (me CompactIPv4NodeInfo) MarshalBinary() ([]byte, error) {
return marshalBinarySlice(slices.Map(func(ni NodeInfo) NodeInfo {
ni.Addr.IP = ni.Addr.IP.To4()
return ni
}, me).(CompactIPv4NodeInfo))
}
func (me CompactIPv4NodeInfo) MarshalBencode() ([]byte, error) {
return bencodeBytesResult(me.MarshalBinary())
}
func (me *CompactIPv4NodeInfo) UnmarshalBinary(b []byte) error {
return unmarshalBinarySlice(me, b)
}
func (me *CompactIPv4NodeInfo) UnmarshalBencode(b []byte) error {
return unmarshalBencodedBinary(me, b)
}

View File

@@ -0,0 +1,30 @@
package krpc
import "github.com/anacrolix/missinggo/slices"
type CompactIPv6NodeAddrs []NodeAddr
func (CompactIPv6NodeAddrs) ElemSize() int { return 18 }
func (me CompactIPv6NodeAddrs) MarshalBinary() ([]byte, error) {
return marshalBinarySlice(slices.Map(func(na NodeAddr) NodeAddr {
na.IP = na.IP.To16()
return na
}, me).(CompactIPv6NodeAddrs))
}
func (me CompactIPv6NodeAddrs) MarshalBencode() ([]byte, error) {
return bencodeBytesResult(me.MarshalBinary())
}
func (me *CompactIPv6NodeAddrs) UnmarshalBinary(b []byte) error {
return unmarshalBinarySlice(me, b)
}
func (me *CompactIPv6NodeAddrs) UnmarshalBencode(b []byte) error {
return unmarshalBencodedBinary(me, b)
}
func (me CompactIPv6NodeAddrs) NodeAddrs() []NodeAddr {
return me
}

View File

@@ -0,0 +1,32 @@
package krpc
import (
"github.com/anacrolix/missinggo/slices"
)
type (
CompactIPv6NodeInfo []NodeInfo
)
func (CompactIPv6NodeInfo) ElemSize() int {
return 38
}
func (me CompactIPv6NodeInfo) MarshalBinary() ([]byte, error) {
return marshalBinarySlice(slices.Map(func(ni NodeInfo) NodeInfo {
ni.Addr.IP = ni.Addr.IP.To16()
return ni
}, me).(CompactIPv6NodeInfo))
}
func (me CompactIPv6NodeInfo) MarshalBencode() ([]byte, error) {
return bencodeBytesResult(me.MarshalBinary())
}
func (me *CompactIPv6NodeInfo) UnmarshalBinary(b []byte) error {
return unmarshalBinarySlice(me, b)
}
func (me *CompactIPv6NodeInfo) UnmarshalBencode(b []byte) error {
return unmarshalBencodedBinary(me, b)
}

View File

@@ -0,0 +1,67 @@
package krpc
import (
"encoding"
"fmt"
"reflect"
"github.com/anacrolix/missinggo/slices"
"github.com/anacrolix/torrent/bencode"
)
func unmarshalBencodedBinary(u encoding.BinaryUnmarshaler, b []byte) (err error) {
var ub string
err = bencode.Unmarshal(b, &ub)
if err != nil {
return
}
return u.UnmarshalBinary([]byte(ub))
}
type elemSizer interface {
ElemSize() int
}
func unmarshalBinarySlice(slice elemSizer, b []byte) (err error) {
sliceValue := reflect.ValueOf(slice).Elem()
elemType := sliceValue.Type().Elem()
bytesPerElem := slice.ElemSize()
for len(b) != 0 {
if len(b) < bytesPerElem {
err = fmt.Errorf("%d trailing bytes < %d required for element", len(b), bytesPerElem)
break
}
elem := reflect.New(elemType)
err = elem.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b[:bytesPerElem])
if err != nil {
return
}
sliceValue.Set(reflect.Append(sliceValue, elem.Elem()))
b = b[bytesPerElem:]
}
return
}
func marshalBinarySlice(slice elemSizer) (ret []byte, err error) {
var elems []encoding.BinaryMarshaler
slices.MakeInto(&elems, slice)
for _, e := range elems {
var b []byte
b, err = e.MarshalBinary()
if err != nil {
return
}
if len(b) != slice.ElemSize() {
panic(fmt.Sprintf("marshalled %d bytes, but expected %d", len(b), slice.ElemSize()))
}
ret = append(ret, b...)
}
return
}
func bencodeBytesResult(b []byte, err error) ([]byte, error) {
if err != nil {
return b, err
}
return bencode.Marshal(b)
}

59
vendor/github.com/anacrolix/dht/krpc/error.go generated vendored Normal file
View File

@@ -0,0 +1,59 @@
package krpc
import (
"fmt"
"github.com/anacrolix/torrent/bencode"
)
var ErrorMethodUnknown = Error{
Code: 204,
Msg: "Method Unknown",
}
// Represented as a string or list in bencode.
type Error struct {
Code int
Msg string
}
var (
_ bencode.Unmarshaler = (*Error)(nil)
_ bencode.Marshaler = (*Error)(nil)
_ error = Error{}
)
func (e *Error) UnmarshalBencode(_b []byte) (err error) {
var _v interface{}
err = bencode.Unmarshal(_b, &_v)
if err != nil {
return
}
switch v := _v.(type) {
case []interface{}:
func() {
defer func() {
r := recover()
if r == nil {
return
}
err = fmt.Errorf("unpacking %#v: %s", v, r)
}()
e.Code = int(v[0].(int64))
e.Msg = v[1].(string)
}()
case string:
e.Msg = v
default:
err = fmt.Errorf(`KRPC error bencode value has unexpected type: %T`, _v)
}
return
}
func (e Error) MarshalBencode() (ret []byte, err error) {
return bencode.Marshal([]interface{}{e.Code, e.Msg})
}
func (e Error) Error() string {
return fmt.Sprintf("KRPC error %d: %s", e.Code, e.Msg)
}

36
vendor/github.com/anacrolix/dht/krpc/id.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package krpc
import (
"fmt"
"github.com/anacrolix/torrent/bencode"
)
type ID [20]byte
var _ interface {
bencode.Marshaler
bencode.Unmarshaler
} = (*ID)(nil)
func IdFromString(s string) (id ID) {
if n := copy(id[:], s); n != 20 {
panic(n)
}
return
}
func (id *ID) MarshalBencode() ([]byte, error) {
return []byte("20:" + string(id[:])), nil
}
func (id *ID) UnmarshalBencode(b []byte) error {
var s string
if err := bencode.Unmarshal(b, &s); err != nil {
return err
}
if n := copy(id[:], s); n != 20 {
return fmt.Errorf("string has wrong length: %d", n)
}
return nil
}

85
vendor/github.com/anacrolix/dht/krpc/msg.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
package krpc
import (
"fmt"
)
// Msg represents messages that nodes in the network send to each other as specified by the protocol.
// They are also refered to as the KRPC messages.
// There are three types of messages: QUERY, RESPONSE, ERROR
// The message is a dictonary that is then
// "bencoded" (serialization & compression format adopted by the BitTorrent)
// and sent via the UDP connection to peers.
//
// A KRPC message is a single dictionary with two keys common to every message and additional keys depending on the type of message.
// Every message has a key "t" with a string value representing a transaction ID.
// This transaction ID is generated by the querying node and is echoed in the response, so responses
// may be correlated with multiple queries to the same node. The transaction ID should be encoded as a short string of binary numbers, typically 2 characters are enough as they cover 2^16 outstanding queries. The other key contained in every KRPC message is "y" with a single character value describing the type of message. The value of the "y" key is one of "q" for query, "r" for response, or "e" for error.
// 3 message types: QUERY, RESPONSE, ERROR
type Msg struct {
Q string `bencode:"q,omitempty"` // Query method (one of 4: "ping", "find_node", "get_peers", "announce_peer")
A *MsgArgs `bencode:"a,omitempty"` // named arguments sent with a query
T string `bencode:"t"` // required: transaction ID
Y string `bencode:"y"` // required: type of the message: q for QUERY, r for RESPONSE, e for ERROR
R *Return `bencode:"r,omitempty"` // RESPONSE type only
E *Error `bencode:"e,omitempty"` // ERROR type only
IP NodeAddr `bencode:"ip,omitempty"`
ReadOnly bool `bencode:"ro,omitempty"`
}
type MsgArgs struct {
ID ID `bencode:"id"` // ID of the querying Node
InfoHash ID `bencode:"info_hash,omitempty"` // InfoHash of the torrent
Target ID `bencode:"target,omitempty"` // ID of the node sought
Token string `bencode:"token,omitempty"` // Token received from an earlier get_peers query
Port int `bencode:"port,omitempty"` // Senders torrent port
ImpliedPort bool `bencode:"implied_port,omitempty"` // Use senders apparent DHT port
Want []Want `bencode:"want,omitempty"` // Contains strings like "n4" and "n6" from BEP 32.
}
type Want string
const (
WantNodes Want = "n4"
WantNodes6 Want = "n6"
)
type Return struct {
ID ID `bencode:"id"` // ID of the querying node
Nodes CompactIPv4NodeInfo `bencode:"nodes,omitempty"` // K closest nodes to the requested target
Nodes6 CompactIPv6NodeInfo `bencode:"nodes6,omitempty"` // K closest nodes to the requested target
Token string `bencode:"token,omitempty"` // Token for future announce_peer
Values []NodeAddr `bencode:"values,omitempty"` // Torrent peers
}
var _ fmt.Stringer = Msg{}
func (m Msg) String() string {
return fmt.Sprintf("%#v", m)
}
// The node ID of the source of this Msg. Returns nil if it isn't present.
// TODO: Can we verify Msgs more aggressively so this is guaranteed to return
// a valid ID for a checked Msg?
func (m Msg) SenderID() *ID {
switch m.Y {
case "q":
if m.A == nil {
return nil
}
return &m.A.ID
case "r":
if m.R == nil {
return nil
}
return &m.R.ID
}
return nil
}
func (m Msg) Error() *Error {
if m.Y != "e" {
return nil
}
return m.E
}

62
vendor/github.com/anacrolix/dht/krpc/nodeaddr.go generated vendored Normal file
View File

@@ -0,0 +1,62 @@
package krpc
import (
"bytes"
"encoding/binary"
"net"
"strconv"
"github.com/anacrolix/torrent/bencode"
)
type NodeAddr struct {
IP net.IP
Port int
}
// A zero Port is taken to mean no port provided, per BEP 7.
func (me NodeAddr) String() string {
if me.Port == 0 {
return me.IP.String()
}
return net.JoinHostPort(me.IP.String(), strconv.FormatInt(int64(me.Port), 10))
}
func (me *NodeAddr) UnmarshalBinary(b []byte) error {
me.IP = make(net.IP, len(b)-2)
copy(me.IP, b[:len(b)-2])
me.Port = int(binary.BigEndian.Uint16(b[len(b)-2:]))
return nil
}
func (me *NodeAddr) UnmarshalBencode(b []byte) (err error) {
var _b []byte
err = bencode.Unmarshal(b, &_b)
if err != nil {
return
}
return me.UnmarshalBinary(_b)
}
func (me NodeAddr) MarshalBinary() ([]byte, error) {
var b bytes.Buffer
b.Write(me.IP)
binary.Write(&b, binary.BigEndian, uint16(me.Port))
return b.Bytes(), nil
}
func (me NodeAddr) MarshalBencode() ([]byte, error) {
return bencodeBytesResult(me.MarshalBinary())
}
func (me NodeAddr) UDP() *net.UDPAddr {
return &net.UDPAddr{
IP: me.IP,
Port: me.Port,
}
}
func (me *NodeAddr) FromUDPAddr(ua *net.UDPAddr) {
me.IP = ua.IP
me.Port = ua.Port
}

46
vendor/github.com/anacrolix/dht/krpc/nodeinfo.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
package krpc
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
"math"
"math/rand"
"net"
)
type NodeInfo struct {
ID [20]byte
Addr NodeAddr
}
func (me NodeInfo) String() string {
return fmt.Sprintf("{%x at %s}", me.ID, me.Addr)
}
func RandomNodeInfo(ipLen int) (ni NodeInfo) {
rand.Read(ni.ID[:])
ni.Addr.IP = make(net.IP, ipLen)
rand.Read(ni.Addr.IP)
ni.Addr.Port = rand.Intn(math.MaxUint16 + 1)
return
}
var _ interface {
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
} = (*NodeInfo)(nil)
func (ni NodeInfo) MarshalBinary() ([]byte, error) {
var w bytes.Buffer
w.Write(ni.ID[:])
w.Write(ni.Addr.IP)
binary.Write(&w, binary.BigEndian, uint16(ni.Addr.Port))
return w.Bytes(), nil
}
func (ni *NodeInfo) UnmarshalBinary(b []byte) error {
copy(ni.ID[:], b)
return ni.Addr.UnmarshalBinary(b[20:])
}

20
vendor/github.com/anacrolix/dht/misc.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
package dht
import (
"net"
)
func mustListen(addr string) net.PacketConn {
ret, err := net.ListenPacket("udp", addr)
if err != nil {
panic(err)
}
return ret
}
func addrResolver(addr string) func() ([]Addr, error) {
return func() ([]Addr, error) {
ua, err := net.ResolveUDPAddr("udp", addr)
return []Addr{NewAddr(ua)}, err
}
}

57
vendor/github.com/anacrolix/dht/node.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package dht
import (
"time"
"github.com/anacrolix/dht/krpc"
)
type nodeKey struct {
addr Addr
id int160
}
type node struct {
nodeKey
announceToken string
readOnly bool
lastGotQuery time.Time
lastGotResponse time.Time
consecutiveFailures int
}
func (n *node) hasAddrAndID(addr Addr, id int160) bool {
return id == n.id && n.addr.String() == addr.String()
}
func (n *node) IsSecure() bool {
return NodeIdSecure(n.id.AsByteArray(), n.addr.UDPAddr().IP)
}
func (n *node) idString() string {
return n.id.ByteString()
}
func (n *node) NodeInfo() (ret krpc.NodeInfo) {
ret.Addr = n.addr.KRPC()
if n := copy(ret.ID[:], n.idString()); n != 20 {
panic(n)
}
return
}
// Per the spec in BEP 5.
func (n *node) IsGood() bool {
if n.id.IsZero() {
return false
}
if time.Since(n.lastGotResponse) < 15*time.Minute {
return true
}
if !n.lastGotResponse.IsZero() && time.Since(n.lastGotQuery) < 15*time.Minute {
return true
}
return false
}

43
vendor/github.com/anacrolix/dht/nodes_file.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
package dht
import (
"io/ioutil"
"os"
"github.com/anacrolix/dht/krpc"
)
func WriteNodesToFile(ns []krpc.NodeInfo, fileName string) (err error) {
b, err := krpc.CompactIPv6NodeInfo(ns).MarshalBinary()
if err != nil {
return
}
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
if err != nil {
return
}
defer func() {
closeErr := f.Close()
if err == nil {
err = closeErr
}
}()
_, err = f.Write(b)
return
}
func ReadNodesFromFile(fileName string) (ns []krpc.NodeInfo, err error) {
f, err := os.Open(fileName)
if err != nil {
return
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return
}
var cnis krpc.CompactIPv6NodeInfo
err = cnis.UnmarshalBinary(b)
ns = cnis
return
}

102
vendor/github.com/anacrolix/dht/security.go generated vendored Normal file
View File

@@ -0,0 +1,102 @@
package dht
import (
"hash/crc32"
"net"
)
func maskForIP(ip net.IP) []byte {
switch {
case ip.To4() != nil:
return []byte{0x03, 0x0f, 0x3f, 0xff}
default:
return []byte{0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}
}
}
// Generate the CRC used to make or validate secure node ID.
func crcIP(ip net.IP, rand uint8) uint32 {
if ip4 := ip.To4(); ip4 != nil {
ip = ip4
}
// Copy IP so we can make changes. Go sux at this.
ip = append(make(net.IP, 0, len(ip)), ip...)
mask := maskForIP(ip)
for i := range mask {
ip[i] &= mask[i]
}
r := rand & 7
ip[0] |= r << 5
return crc32.Checksum(ip[:len(mask)], crc32.MakeTable(crc32.Castagnoli))
}
// Makes a node ID secure, in-place. The ID is 20 raw bytes.
// http://www.libtorrent.org/dht_sec.html
func SecureNodeId(id *[20]byte, ip net.IP) {
crc := crcIP(ip, id[19])
id[0] = byte(crc >> 24 & 0xff)
id[1] = byte(crc >> 16 & 0xff)
id[2] = byte(crc>>8&0xf8) | id[2]&7
}
// Returns whether the node ID is considered secure. The id is the 20 raw
// bytes. http://www.libtorrent.org/dht_sec.html
func NodeIdSecure(id [20]byte, ip net.IP) bool {
if isLocalNetwork(ip) {
return true
}
if ip4 := ip.To4(); ip4 != nil {
ip = ip4
}
crc := crcIP(ip, id[19])
if id[0] != byte(crc>>24&0xff) {
return false
}
if id[1] != byte(crc>>16&0xff) {
return false
}
if id[2]&0xf8 != byte(crc>>8&0xf8) {
return false
}
return true
}
var (
classA, classB, classC *net.IPNet
)
func mustParseCIDRIPNet(s string) *net.IPNet {
_, ret, err := net.ParseCIDR(s)
if err != nil {
panic(err)
}
return ret
}
func init() {
classA = mustParseCIDRIPNet("10.0.0.0/8")
classB = mustParseCIDRIPNet("172.16.0.0/12")
classC = mustParseCIDRIPNet("192.168.0.0/16")
}
// Per http://www.libtorrent.org/dht_sec.html#enforcement, the IP is
// considered a local network address and should be exempted from node ID
// verification.
func isLocalNetwork(ip net.IP) bool {
if classA.Contains(ip) {
return true
}
if classB.Contains(ip) {
return true
}
if classC.Contains(ip) {
return true
}
if ip.IsLinkLocalUnicast() {
return true
}
if ip.IsLoopback() {
return true
}
return false
}

813
vendor/github.com/anacrolix/dht/server.go generated vendored Normal file
View File

@@ -0,0 +1,813 @@
package dht
import (
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
"sync"
"text/tabwriter"
"time"
"github.com/anacrolix/missinggo"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/iplist"
"github.com/anacrolix/torrent/logonce"
"github.com/anacrolix/torrent/metainfo"
"github.com/anacrolix/dht/krpc"
)
// A Server defines parameters for a DHT node server that is able to send
// queries, and respond to the ones from the network. Each node has a globally
// unique identifier known as the "node ID." Node IDs are chosen at random
// from the same 160-bit space as BitTorrent infohashes and define the
// behaviour of the node. Zero valued Server does not have a valid ID and thus
// is unable to function properly. Use `NewServer(nil)` to initialize a
// default node.
type Server struct {
id int160
socket net.PacketConn
mu sync.RWMutex
transactions map[transactionKey]*Transaction
nextT uint64 // unique "t" field for outbound queries
table table
closed missinggo.Event
ipBlockList iplist.Ranger
tokenServer tokenServer // Manages tokens we issue to our queriers.
config ServerConfig
stats ServerStats
}
func (s *Server) numGoodNodes() (num int) {
s.table.forNodes(func(n *node) bool {
if n.IsGood() {
num++
}
return true
})
return
}
func prettySince(t time.Time) string {
if t.IsZero() {
return "never"
}
d := time.Since(t)
d /= time.Second
d *= time.Second
return fmt.Sprintf("%s ago", d)
}
func (s *Server) WriteStatus(w io.Writer) {
fmt.Fprintf(w, "Listening on %s\n", s.Addr())
s.mu.Lock()
defer s.mu.Unlock()
fmt.Fprintf(w, "Nodes in table: %d good, %d total\n", s.numGoodNodes(), s.numNodes())
fmt.Fprintf(w, "Ongoing transactions: %d\n", len(s.transactions))
fmt.Fprintf(w, "Server node ID: %x\n", s.id.Bytes())
fmt.Fprintln(w)
tw := tabwriter.NewWriter(w, 0, 0, 1, ' ', 0)
fmt.Fprintf(tw, "b#\tnode id\taddr\tanntok\tlast query\tlast response\tcf\n")
for i, b := range s.table.buckets {
b.EachNode(func(n *node) bool {
fmt.Fprintf(tw, "%d\t%x\t%s\t%v\t%s\t%s\t%d\n",
i,
n.id.Bytes(),
n.addr,
len(n.announceToken),
prettySince(n.lastGotQuery),
prettySince(n.lastGotResponse),
n.consecutiveFailures,
)
return true
})
}
tw.Flush()
}
func (s *Server) numNodes() (num int) {
s.table.forNodes(func(n *node) bool {
num++
return true
})
return
}
// Stats returns statistics for the server.
func (s *Server) Stats() ServerStats {
s.mu.Lock()
defer s.mu.Unlock()
ss := s.stats
ss.GoodNodes = s.numGoodNodes()
ss.Nodes = s.numNodes()
ss.OutstandingTransactions = len(s.transactions)
return ss
}
// Addr returns the listen address for the server. Packets arriving to this address
// are processed by the server (unless aliens are involved).
func (s *Server) Addr() net.Addr {
return s.socket.LocalAddr()
}
// NewServer initializes a new DHT node server.
func NewServer(c *ServerConfig) (s *Server, err error) {
if c == nil {
c = &ServerConfig{
Conn: mustListen(":0"),
NoSecurity: true,
StartingNodes: GlobalBootstrapAddrs,
}
}
if missinggo.IsZeroValue(c.NodeId) {
c.NodeId = RandomNodeID()
if !c.NoSecurity && c.PublicIP != nil {
SecureNodeId(&c.NodeId, c.PublicIP)
}
}
s = &Server{
config: *c,
ipBlockList: c.IPBlocklist,
tokenServer: tokenServer{
maxIntervalDelta: 2,
interval: 5 * time.Minute,
secret: make([]byte, 20),
},
transactions: make(map[transactionKey]*Transaction),
table: table{
k: 8,
},
}
rand.Read(s.tokenServer.secret)
s.socket = c.Conn
s.id = int160FromByteArray(c.NodeId)
s.table.rootID = s.id
go func() {
err := s.serve()
s.mu.Lock()
defer s.mu.Unlock()
if s.closed.IsSet() {
return
}
if err != nil {
panic(err)
}
}()
return
}
// Returns a description of the Server. Python repr-style.
func (s *Server) String() string {
return fmt.Sprintf("dht server on %s", s.socket.LocalAddr())
}
// Packets to and from any address matching a range in the list are dropped.
func (s *Server) SetIPBlockList(list iplist.Ranger) {
s.mu.Lock()
defer s.mu.Unlock()
s.ipBlockList = list
}
func (s *Server) IPBlocklist() iplist.Ranger {
return s.ipBlockList
}
func (s *Server) processPacket(b []byte, addr Addr) {
if len(b) < 2 || b[0] != 'd' || b[len(b)-1] != 'e' {
// KRPC messages are bencoded dicts.
readNotKRPCDict.Add(1)
return
}
var d krpc.Msg
err := bencode.Unmarshal(b, &d)
if err != nil {
readUnmarshalError.Add(1)
func() {
if se, ok := err.(*bencode.SyntaxError); ok {
// The message was truncated.
if int(se.Offset) == len(b) {
return
}
// Some messages seem to drop to nul chars abrubtly.
if int(se.Offset) < len(b) && b[se.Offset] == 0 {
return
}
// The message isn't bencode from the first.
if se.Offset == 0 {
return
}
}
// if missinggo.CryHeard() {
// log.Printf("%s: received bad krpc message from %s: %s: %+q", s, addr, err, b)
// }
}()
return
}
s.mu.Lock()
defer s.mu.Unlock()
if s.closed.IsSet() {
return
}
var n *node
if sid := d.SenderID(); sid != nil {
n, _ = s.getNode(addr, int160FromByteArray(*sid), !d.ReadOnly)
if n != nil && d.ReadOnly {
n.readOnly = true
}
}
if d.Y == "q" {
readQuery.Add(1)
s.handleQuery(addr, d)
return
}
t := s.findResponseTransaction(d.T, addr)
if t == nil {
return
}
go t.handleResponse(d)
if n != nil {
n.lastGotResponse = time.Now()
n.consecutiveFailures = 0
}
s.deleteTransaction(t)
}
func (s *Server) serve() error {
var b [0x10000]byte
for {
n, addr, err := s.socket.ReadFrom(b[:])
if err != nil {
return err
}
read.Add(1)
if n == len(b) {
logonce.Stderr.Printf("received dht packet exceeds buffer size")
continue
}
if missinggo.AddrPort(addr) == 0 {
readZeroPort.Add(1)
continue
}
s.mu.Lock()
blocked := s.ipBlocked(missinggo.AddrIP(addr))
s.mu.Unlock()
if blocked {
readBlocked.Add(1)
continue
}
s.processPacket(b[:n], NewAddr(addr.(*net.UDPAddr)))
}
}
func (s *Server) ipBlocked(ip net.IP) (blocked bool) {
if s.ipBlockList == nil {
return
}
_, blocked = s.ipBlockList.Lookup(ip)
return
}
// Adds directly to the node table.
func (s *Server) AddNode(ni krpc.NodeInfo) error {
id := int160FromByteArray(ni.ID)
if id.IsZero() {
return s.Ping(ni.Addr.UDP(), nil)
}
_, err := s.getNode(NewAddr(ni.Addr.UDP()), int160FromByteArray(ni.ID), true)
return err
}
func wantsContain(ws []krpc.Want, w krpc.Want) bool {
for _, _w := range ws {
if _w == w {
return true
}
}
return false
}
func shouldReturnNodes(queryWants []krpc.Want, querySource net.IP) bool {
if len(queryWants) != 0 {
return wantsContain(queryWants, krpc.WantNodes)
}
return querySource.To4() != nil
}
func shouldReturnNodes6(queryWants []krpc.Want, querySource net.IP) bool {
if len(queryWants) != 0 {
return wantsContain(queryWants, krpc.WantNodes6)
}
return querySource.To4() == nil
}
func (s *Server) makeReturnNodes(target int160, filter func(krpc.NodeAddr) bool) []krpc.NodeInfo {
return s.closestGoodNodeInfos(8, target, filter)
}
func (s *Server) setReturnNodes(r *krpc.Return, queryMsg krpc.Msg, querySource Addr) {
target := int160FromByteArray(queryMsg.A.InfoHash)
if shouldReturnNodes(queryMsg.A.Want, querySource.UDPAddr().IP) {
r.Nodes = s.makeReturnNodes(target, func(na krpc.NodeAddr) bool { return na.IP.To4() != nil })
}
if shouldReturnNodes6(queryMsg.A.Want, querySource.UDPAddr().IP) {
r.Nodes6 = s.makeReturnNodes(target, func(krpc.NodeAddr) bool { return true })
}
}
// TODO: Probably should write error messages back to senders if something is
// wrong.
func (s *Server) handleQuery(source Addr, m krpc.Msg) {
if m.SenderID() != nil {
if n, _ := s.getNode(source, int160FromByteArray(*m.SenderID()), !m.ReadOnly); n != nil {
n.lastGotQuery = time.Now()
}
}
if s.config.OnQuery != nil {
propagate := s.config.OnQuery(&m, source.UDPAddr())
if !propagate {
return
}
}
// Don't respond.
if s.config.Passive {
return
}
// TODO: Should we disallow replying to ourself?
args := m.A
switch m.Q {
case "ping":
s.reply(source, m.T, krpc.Return{})
case "get_peers":
var r krpc.Return
// TODO: Return nodes.
s.setReturnNodes(&r, m, source)
r.Token = s.createToken(source)
s.reply(source, m.T, r)
case "find_node":
var r krpc.Return
s.setReturnNodes(&r, m, source)
s.reply(source, m.T, r)
case "announce_peer":
readAnnouncePeer.Add(1)
if !s.validToken(args.Token, source) {
expvars.Add("received announce_peer with invalid token", 1)
return
}
expvars.Add("received announce_peer with valid token", 1)
if h := s.config.OnAnnouncePeer; h != nil {
p := Peer{
IP: source.UDPAddr().IP,
Port: args.Port,
}
if args.ImpliedPort {
p.Port = source.UDPAddr().Port
}
go h(metainfo.Hash(args.InfoHash), p)
}
s.reply(source, m.T, krpc.Return{})
default:
s.sendError(source, m.T, krpc.ErrorMethodUnknown)
}
}
func (s *Server) sendError(addr Addr, t string, e krpc.Error) {
m := krpc.Msg{
T: t,
Y: "e",
E: &e,
}
b, err := bencode.Marshal(m)
if err != nil {
panic(err)
}
err = s.writeToNode(b, addr)
if err != nil {
log.Printf("error replying to %s: %s", addr, err)
}
}
func (s *Server) reply(addr Addr, t string, r krpc.Return) {
expvars.Add("replied to peer", 1)
r.ID = s.id.AsByteArray()
m := krpc.Msg{
T: t,
Y: "r",
R: &r,
IP: addr.KRPC(),
}
b, err := bencode.Marshal(m)
if err != nil {
panic(err)
}
err = s.writeToNode(b, addr)
if err != nil {
log.Printf("error replying to %s: %s", addr, err)
}
}
// Returns the node if it's in the routing table, adding it if appropriate.
func (s *Server) getNode(addr Addr, id int160, tryAdd bool) (*node, error) {
if n := s.table.getNode(addr, id); n != nil {
return n, nil
}
n := &node{nodeKey: nodeKey{
id: id,
addr: addr,
}}
// Check that the node would be good to begin with. (It might have a bad
// ID or banned address, or we fucked up the initial node field
// invariant.)
if err := s.nodeErr(n); err != nil {
return nil, err
}
if !tryAdd {
return nil, errors.New("node not present and add flag false")
}
b := s.table.bucketForID(id)
if b.Len() >= s.table.k {
if b.EachNode(func(n *node) bool {
if s.nodeIsBad(n) {
s.table.dropNode(n)
}
return b.Len() >= s.table.k
}) {
// No room.
return nil, errors.New("no room in bucket")
}
}
if err := s.table.addNode(n); err != nil {
panic(fmt.Sprintf("expected to add node: %s", err))
}
return n, nil
}
func (s *Server) nodeIsBad(n *node) bool {
return s.nodeErr(n) != nil
}
func (s *Server) nodeErr(n *node) error {
if n.id == s.id {
return errors.New("is self")
}
if n.id.IsZero() {
return errors.New("has zero id")
}
if !s.config.NoSecurity && !n.IsSecure() {
return errors.New("not secure")
}
if n.IsGood() {
return nil
}
if n.consecutiveFailures >= 3 {
return fmt.Errorf("has %d consecutive failures", n.consecutiveFailures)
}
return nil
}
func (s *Server) writeToNode(b []byte, node Addr) (err error) {
if list := s.ipBlockList; list != nil {
if r, ok := list.Lookup(missinggo.AddrIP(node.UDPAddr())); ok {
err = fmt.Errorf("write to %s blocked: %s", node, r.Description)
return
}
}
// log.Printf("writing to %s: %q", node.UDPAddr(), b)
n, err := s.socket.WriteTo(b, node.UDPAddr())
writes.Add(1)
if err != nil {
writeErrors.Add(1)
err = fmt.Errorf("error writing %d bytes to %s: %s", len(b), node, err)
return
}
if n != len(b) {
err = io.ErrShortWrite
return
}
return
}
func (s *Server) findResponseTransaction(transactionID string, sourceNode Addr) *Transaction {
return s.transactions[transactionKey{
sourceNode.String(),
transactionID}]
}
func (s *Server) nextTransactionID() string {
var b [binary.MaxVarintLen64]byte
n := binary.PutUvarint(b[:], s.nextT)
s.nextT++
return string(b[:n])
}
func (s *Server) deleteTransaction(t *Transaction) {
delete(s.transactions, t.key())
}
func (s *Server) deleteTransactionUnlocked(t *Transaction) {
s.mu.Lock()
defer s.mu.Unlock()
s.deleteTransaction(t)
}
func (s *Server) addTransaction(t *Transaction) {
if _, ok := s.transactions[t.key()]; ok {
panic("transaction not unique")
}
s.transactions[t.key()] = t
}
// ID returns the 20-byte server ID. This is the ID used to communicate with the
// DHT network.
func (s *Server) ID() [20]byte {
return s.id.AsByteArray()
}
func (s *Server) createToken(addr Addr) string {
return s.tokenServer.CreateToken(addr)
}
func (s *Server) validToken(token string, addr Addr) bool {
return s.tokenServer.ValidToken(token, addr)
}
func (s *Server) query(addr Addr, q string, a *krpc.MsgArgs, callback func(krpc.Msg, error)) error {
tid := s.nextTransactionID()
if a == nil {
a = &krpc.MsgArgs{}
}
if callback == nil {
callback = func(krpc.Msg, error) {}
}
a.ID = s.ID()
m := krpc.Msg{
T: tid,
Y: "q",
Q: q,
A: a,
}
// BEP 43. Outgoing queries from passive nodes should contain "ro":1 in
// the top level dictionary.
if s.config.Passive {
m.ReadOnly = true
}
b, err := bencode.Marshal(m)
if err != nil {
return err
}
var t *Transaction
t = &Transaction{
remoteAddr: addr,
t: tid,
querySender: func() error {
return s.writeToNode(b, addr)
},
onResponse: func(m krpc.Msg) {
go callback(m, nil)
go s.deleteTransactionUnlocked(t)
},
onTimeout: func() {
go callback(krpc.Msg{}, errors.New("query timed out"))
s.mu.Lock()
defer s.mu.Unlock()
s.deleteTransaction(t)
for _, n := range s.table.addrNodes(addr) {
n.consecutiveFailures++
}
},
onSendError: func(err error) {
go callback(krpc.Msg{}, fmt.Errorf("error resending query: %s", err))
s.mu.Lock()
defer s.mu.Unlock()
s.deleteTransaction(t)
for _, n := range s.table.addrNodes(addr) {
n.consecutiveFailures++
}
},
queryResendDelay: func() time.Duration {
if s.config.QueryResendDelay != nil {
return s.config.QueryResendDelay()
}
return defaultQueryResendDelay()
},
}
s.stats.OutboundQueriesAttempted++
err = t.sendQuery()
if err != nil {
return err
}
// s.getNode(node, "").lastSentQuery = time.Now()
t.mu.Lock()
t.startResendTimer()
t.mu.Unlock()
s.addTransaction(t)
return nil
}
// Sends a ping query to the address given.
func (s *Server) Ping(node *net.UDPAddr, callback func(krpc.Msg, error)) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.ping(node, callback)
}
func (s *Server) ping(node *net.UDPAddr, callback func(krpc.Msg, error)) error {
return s.query(NewAddr(node), "ping", nil, callback)
}
func (s *Server) announcePeer(node Addr, infoHash int160, port int, token string, impliedPort bool, callback func(krpc.Msg, error)) error {
if port == 0 && !impliedPort {
return errors.New("nothing to announce")
}
return s.query(node, "announce_peer", &krpc.MsgArgs{
ImpliedPort: impliedPort,
InfoHash: infoHash.AsByteArray(),
Port: port,
Token: token,
}, func(m krpc.Msg, err error) {
if callback != nil {
go callback(m, err)
}
if err := m.Error(); err != nil {
announceErrors.Add(1)
return
}
s.mu.Lock()
defer s.mu.Unlock()
s.stats.SuccessfulOutboundAnnouncePeerQueries++
})
}
// Add response nodes to node table.
func (s *Server) addResponseNodes(d krpc.Msg) {
if d.R == nil {
return
}
for _, cni := range d.R.Nodes {
s.getNode(NewAddr(cni.Addr.UDP()), int160FromByteArray(cni.ID), true)
}
}
// Sends a find_node query to addr. targetID is the node we're looking for.
func (s *Server) findNode(addr Addr, targetID int160, callback func(krpc.Msg, error)) (err error) {
return s.query(addr, "find_node", &krpc.MsgArgs{
Target: targetID.AsByteArray(),
Want: []krpc.Want{krpc.WantNodes, krpc.WantNodes6},
}, func(m krpc.Msg, err error) {
// Scrape peers from the response to put in the server's table before
// handing the response back to the caller.
s.mu.Lock()
s.addResponseNodes(m)
s.mu.Unlock()
callback(m, err)
})
}
type TraversalStats struct {
NumAddrsTried int
NumResponses int
}
// Populates the node table.
func (s *Server) Bootstrap() (ts TraversalStats, err error) {
initialAddrs, err := s.traversalStartingAddrs()
if err != nil {
return
}
var outstanding sync.WaitGroup
triedAddrs := newBloomFilterForTraversal()
var onAddr func(addr Addr)
onAddr = func(addr Addr) {
if triedAddrs.Test([]byte(addr.String())) {
return
}
ts.NumAddrsTried++
outstanding.Add(1)
triedAddrs.AddString(addr.String())
s.findNode(addr, s.id, func(m krpc.Msg, err error) {
defer outstanding.Done()
s.mu.Lock()
defer s.mu.Unlock()
if err != nil {
return
}
ts.NumResponses++
if r := m.R; r != nil {
for _, addr := range r.Nodes {
onAddr(NewAddr(addr.Addr.UDP()))
}
}
})
}
s.mu.Lock()
for _, addr := range initialAddrs {
onAddr(addr)
}
s.mu.Unlock()
outstanding.Wait()
return
}
// Returns how many nodes are in the node table.
func (s *Server) NumNodes() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.numNodes()
}
// Exports the current node table.
func (s *Server) Nodes() (nis []krpc.NodeInfo) {
s.mu.Lock()
defer s.mu.Unlock()
s.table.forNodes(func(n *node) bool {
nis = append(nis, krpc.NodeInfo{
Addr: n.addr.KRPC(),
ID: n.id.AsByteArray(),
})
return true
})
return
}
// Stops the server network activity. This is all that's required to clean-up a Server.
func (s *Server) Close() {
s.mu.Lock()
defer s.mu.Unlock()
s.closed.Set()
s.socket.Close()
}
func (s *Server) getPeers(addr Addr, infoHash int160, callback func(krpc.Msg, error)) (err error) {
return s.query(addr, "get_peers", &krpc.MsgArgs{
InfoHash: infoHash.AsByteArray(),
Want: []krpc.Want{krpc.WantNodes, krpc.WantNodes6},
}, func(m krpc.Msg, err error) {
go callback(m, err)
s.mu.Lock()
defer s.mu.Unlock()
s.addResponseNodes(m)
if m.R != nil && m.R.Token != "" && m.SenderID() != nil {
if n, _ := s.getNode(addr, int160FromByteArray(*m.SenderID()), false); n != nil {
n.announceToken = m.R.Token
}
}
})
}
func (s *Server) closestGoodNodeInfos(
k int,
targetID int160,
filter func(krpc.NodeAddr) bool,
) (
ret []krpc.NodeInfo,
) {
for _, n := range s.closestNodes(k, targetID, func(n *node) bool {
return n.IsGood() && filter(n.NodeInfo().Addr)
}) {
ret = append(ret, n.NodeInfo())
}
return
}
func (s *Server) closestNodes(k int, target int160, filter func(*node) bool) []*node {
return s.table.closestNodes(k, target, filter)
}
func (s *Server) traversalStartingAddrs() (addrs []Addr, err error) {
s.mu.RLock()
s.table.forNodes(func(n *node) bool {
addrs = append(addrs, n.addr)
return true
})
s.mu.RUnlock()
if len(addrs) > 0 {
return
}
if s.config.StartingNodes != nil {
addrs, err = s.config.StartingNodes()
if err != nil {
return
}
}
if len(addrs) == 0 {
err = errors.New("no initial nodes")
}
return
}
func (s *Server) AddNodesFromFile(fileName string) (added int, err error) {
ns, err := ReadNodesFromFile(fileName)
if err != nil {
return
}
for _, n := range ns {
if s.AddNode(n) == nil {
added++
}
}
return
}

117
vendor/github.com/anacrolix/dht/table.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
package dht
import "errors"
// Node table, with indexes on distance from root ID to bucket, and node addr.
type table struct {
rootID int160
k int
buckets [160]bucket
addrs map[string]map[int160]struct{}
}
func (tbl *table) addrNodes(addr Addr) []*node {
a := tbl.addrs[addr.String()]
ret := make([]*node, 0, len(a))
for id := range a {
ret = append(ret, tbl.getNode(addr, id))
}
return ret
}
func (tbl *table) dropNode(n *node) {
as := n.addr.String()
if _, ok := tbl.addrs[as][n.id]; !ok {
panic("missing id for addr")
}
delete(tbl.addrs[as], n.id)
if len(tbl.addrs[as]) == 0 {
delete(tbl.addrs, as)
}
b := tbl.bucketForID(n.id)
if _, ok := b.nodes[n]; !ok {
panic("expected node in bucket")
}
delete(b.nodes, n)
}
func (tbl *table) bucketForID(id int160) *bucket {
return &tbl.buckets[tbl.bucketIndex(id)]
}
func (tbl *table) numNodes() (num int) {
for _, b := range tbl.buckets {
num += b.Len()
}
return
}
func (tbl *table) bucketIndex(id int160) int {
if id == tbl.rootID {
panic("nobody puts the root ID in a bucket")
}
var a int160
a.Xor(&tbl.rootID, &id)
index := 160 - a.BitLen()
return index
}
func (tbl *table) forNodes(f func(*node) bool) bool {
for _, b := range tbl.buckets {
if !b.EachNode(f) {
return false
}
}
return true
}
func (tbl *table) getNode(addr Addr, id int160) *node {
if id == tbl.rootID {
return nil
}
return tbl.buckets[tbl.bucketIndex(id)].GetNode(addr, id)
}
func (tbl *table) closestNodes(k int, target int160, filter func(*node) bool) (ret []*node) {
for bi := func() int {
if target == tbl.rootID {
return len(tbl.buckets) - 1
} else {
return tbl.bucketIndex(target)
}
}(); bi >= 0 && len(ret) < k; bi-- {
for n := range tbl.buckets[bi].nodes {
if filter(n) {
ret = append(ret, n)
}
}
}
// TODO: Keep only the closest.
if len(ret) > k {
ret = ret[:k]
}
return
}
func (tbl *table) addNode(n *node) error {
if n.id == tbl.rootID {
return errors.New("is root id")
}
b := &tbl.buckets[tbl.bucketIndex(n.id)]
if b.GetNode(n.addr, n.id) != nil {
return errors.New("already present")
}
if b.Len() >= tbl.k {
return errors.New("bucket is full")
}
b.AddNode(n, tbl.k)
if tbl.addrs == nil {
tbl.addrs = make(map[string]map[int160]struct{}, 160*tbl.k)
}
as := n.addr.String()
if tbl.addrs[as] == nil {
tbl.addrs[as] = make(map[int160]struct{}, 1)
}
tbl.addrs[as][n.id] = struct{}{}
return nil
}

57
vendor/github.com/anacrolix/dht/tokens.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package dht
import (
"crypto/sha1"
"encoding/binary"
"time"
"github.com/bradfitz/iter"
)
// Manages creation and validation of tokens issued to querying nodes.
type tokenServer struct {
// Something only we know that peers can't guess, so they can't deduce valid tokens.
secret []byte
// How long between token changes.
interval time.Duration
// How many intervals may pass between the current interval, and one used to generate a token before it is invalid.
maxIntervalDelta int
timeNow func() time.Time
}
func (me tokenServer) CreateToken(addr Addr) string {
return me.createToken(addr, me.getTimeNow())
}
func (me tokenServer) createToken(addr Addr, t time.Time) string {
h := sha1.New()
ip := addr.UDPAddr().IP.To16()
if len(ip) != 16 {
panic(ip)
}
h.Write(ip)
ti := t.UnixNano() / int64(me.interval)
var b [8]byte
binary.BigEndian.PutUint64(b[:], uint64(ti))
h.Write(b[:])
h.Write(me.secret)
return string(h.Sum(nil))
}
func (me *tokenServer) ValidToken(token string, addr Addr) bool {
t := me.getTimeNow()
for range iter.N(me.maxIntervalDelta + 1) {
if me.createToken(addr, t) == token {
return true
}
t = t.Add(-me.interval)
}
return false
}
func (me *tokenServer) getTimeNow() time.Time {
if me.timeNow == nil {
return time.Now()
}
return me.timeNow()
}

72
vendor/github.com/anacrolix/dht/transaction.go generated vendored Normal file
View File

@@ -0,0 +1,72 @@
package dht
import (
"sync"
"time"
"github.com/anacrolix/dht/krpc"
)
// Transaction keeps track of a message exchange between nodes, such as a
// query message and a response message.
type Transaction struct {
remoteAddr Addr
t string
onResponse func(krpc.Msg)
onTimeout func()
onSendError func(error)
querySender func() error
queryResendDelay func() time.Duration
mu sync.Mutex
gotResponse bool
timer *time.Timer
retries int
lastSend time.Time
}
func (t *Transaction) handleResponse(m krpc.Msg) {
t.mu.Lock()
t.gotResponse = true
t.mu.Unlock()
t.onResponse(m)
}
func (t *Transaction) key() transactionKey {
return transactionKey{
t.remoteAddr.String(),
t.t,
}
}
func (t *Transaction) startResendTimer() {
t.timer = time.AfterFunc(t.queryResendDelay(), t.resendCallback)
}
func (t *Transaction) resendCallback() {
t.mu.Lock()
defer t.mu.Unlock()
if t.gotResponse {
return
}
if t.retries == 2 {
go t.onTimeout()
return
}
t.retries++
if err := t.sendQuery(); err != nil {
go t.onSendError(err)
return
}
if t.timer.Reset(t.queryResendDelay()) {
panic("timer should have fired to get here")
}
}
func (t *Transaction) sendQuery() error {
if err := t.querySender(); err != nil {
return err
}
t.lastSend = time.Now()
return nil
}