362
vendor/github.com/anacrolix/dht/v2/LICENSE
generated
vendored
Normal file
362
vendor/github.com/anacrolix/dht/v2/LICENSE
generated
vendored
Normal 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.
|
||||
21
vendor/github.com/anacrolix/dht/v2/README.md
generated
vendored
Normal file
21
vendor/github.com/anacrolix/dht/v2/README.md
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# dht
|
||||
|
||||
[](https://pkg.go.dev/github.com/anacrolix/dht/v2)
|
||||
|
||||
## Installation
|
||||
|
||||
Get the library package with `go get github.com/anacrolix/dht/v2`, or the provided cmds with `go install github.com/anacrolix/dht/v2/cmd/...@latest`.
|
||||
|
||||
## Commands
|
||||
|
||||
Here I'll describe what some of the provided commands in `./cmd` do.
|
||||
|
||||
### dht-ping
|
||||
|
||||
Pings DHT nodes with the given network addresses.
|
||||
|
||||
$ go run ./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%)
|
||||
61
vendor/github.com/anacrolix/dht/v2/addr.go
generated
vendored
Normal file
61
vendor/github.com/anacrolix/dht/v2/addr.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/missinggo/v2"
|
||||
|
||||
"github.com/anacrolix/dht/v2/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 {
|
||||
Raw() net.Addr
|
||||
Port() int
|
||||
IP() net.IP
|
||||
String() string
|
||||
KRPC() krpc.NodeAddr
|
||||
}
|
||||
|
||||
// Speeds up some of the commonly called Addr methods.
|
||||
type cachedAddr struct {
|
||||
raw net.Addr
|
||||
port int
|
||||
ip net.IP
|
||||
s string
|
||||
}
|
||||
|
||||
func (ca cachedAddr) String() string {
|
||||
return ca.s
|
||||
}
|
||||
|
||||
func (ca cachedAddr) KRPC() krpc.NodeAddr {
|
||||
return krpc.NodeAddr{
|
||||
IP: ca.ip,
|
||||
Port: ca.port,
|
||||
}
|
||||
}
|
||||
|
||||
func (ca cachedAddr) IP() net.IP {
|
||||
return ca.ip
|
||||
}
|
||||
|
||||
func (ca cachedAddr) Port() int {
|
||||
return ca.port
|
||||
}
|
||||
|
||||
func (ca cachedAddr) Raw() net.Addr {
|
||||
return ca.raw
|
||||
}
|
||||
|
||||
func NewAddr(raw net.Addr) Addr {
|
||||
return cachedAddr{
|
||||
raw: raw,
|
||||
s: raw.String(),
|
||||
ip: missinggo.AddrIP(raw),
|
||||
port: missinggo.AddrPort(raw),
|
||||
}
|
||||
}
|
||||
215
vendor/github.com/anacrolix/dht/v2/announce.go
generated
vendored
Normal file
215
vendor/github.com/anacrolix/dht/v2/announce.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package dht
|
||||
|
||||
// get_peers and announce_peers.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/anacrolix/chansync"
|
||||
"github.com/anacrolix/chansync/events"
|
||||
"github.com/anacrolix/dht/v2/traversal"
|
||||
"github.com/anacrolix/log"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
dhtutil "github.com/anacrolix/dht/v2/k-nearest-nodes"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
// Maintains state for an ongoing Announce operation. An Announce is started by calling
|
||||
// Server.Announce.
|
||||
type Announce struct {
|
||||
Peers chan PeersValues
|
||||
|
||||
server *Server
|
||||
infoHash int160.T // Target
|
||||
|
||||
announcePeerOpts *AnnouncePeerOpts
|
||||
scrape bool
|
||||
peerAnnounced chansync.SetOnce
|
||||
|
||||
traversal *traversal.Operation
|
||||
|
||||
closed chansync.SetOnce
|
||||
}
|
||||
|
||||
func (a *Announce) String() string {
|
||||
return fmt.Sprintf("%[1]T %[1]p of %v on %v", a, a.infoHash, a.server)
|
||||
}
|
||||
|
||||
// Returns the number of distinct remote addresses the announce has queried.
|
||||
func (a *Announce) NumContacted() uint32 {
|
||||
return atomic.LoadUint32(&a.traversal.Stats().NumAddrsTried)
|
||||
}
|
||||
|
||||
// Server.Announce option
|
||||
type AnnounceOpt func(a *Announce)
|
||||
|
||||
// Scrape BEP 33 bloom filters in queries.
|
||||
func Scrape() AnnounceOpt {
|
||||
return func(a *Announce) {
|
||||
a.scrape = true
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments for announce_peer from a Server.Announce.
|
||||
type AnnouncePeerOpts struct {
|
||||
// The peer port that we're announcing.
|
||||
Port int
|
||||
// The peer port should be determined by the receiver to be the source port of the query packet.
|
||||
ImpliedPort bool
|
||||
}
|
||||
|
||||
// Finish an Announce get_peers traversal with an announce of a local peer.
|
||||
func AnnouncePeer(opts AnnouncePeerOpts) AnnounceOpt {
|
||||
return func(a *Announce) {
|
||||
a.announcePeerOpts = &opts
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: Use Server.AnnounceTraversal.
|
||||
// Traverses the DHT graph toward nodes that store peers for the infohash, streaming them to the
|
||||
// caller, and announcing the local node to each responding node if port is non-zero or impliedPort
|
||||
// is true.
|
||||
func (s *Server) Announce(infoHash [20]byte, port int, impliedPort bool, opts ...AnnounceOpt) (_ *Announce, err error) {
|
||||
if port != 0 || impliedPort {
|
||||
opts = append([]AnnounceOpt{AnnouncePeer(AnnouncePeerOpts{
|
||||
Port: port,
|
||||
ImpliedPort: impliedPort,
|
||||
})}, opts...)
|
||||
}
|
||||
return s.AnnounceTraversal(infoHash, opts...)
|
||||
}
|
||||
|
||||
// Traverses the DHT graph toward nodes that store peers for the infohash, streaming them to the
|
||||
// caller.
|
||||
func (s *Server) AnnounceTraversal(infoHash [20]byte, opts ...AnnounceOpt) (_ *Announce, err error) {
|
||||
infoHashInt160 := int160.FromByteArray(infoHash)
|
||||
a := &Announce{
|
||||
Peers: make(chan PeersValues),
|
||||
server: s,
|
||||
infoHash: infoHashInt160,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(a)
|
||||
}
|
||||
a.traversal = traversal.Start(traversal.OperationInput{
|
||||
Target: infoHash,
|
||||
DoQuery: a.getPeers,
|
||||
NodeFilter: s.TraversalNodeFilter,
|
||||
})
|
||||
nodes, err := s.TraversalStartingNodes()
|
||||
if err != nil {
|
||||
a.traversal.Stop()
|
||||
return
|
||||
}
|
||||
a.traversal.AddNodes(nodes)
|
||||
go func() {
|
||||
<-a.traversal.Stalled()
|
||||
a.traversal.Stop()
|
||||
<-a.traversal.Stopped()
|
||||
if a.announcePeerOpts != nil {
|
||||
a.announceClosest()
|
||||
}
|
||||
a.peerAnnounced.Set()
|
||||
close(a.Peers)
|
||||
}()
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *Announce) announceClosest() {
|
||||
var wg sync.WaitGroup
|
||||
a.traversal.Closest().Range(func(elem dhtutil.Elem) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
a.logger().Levelf(log.Debug,
|
||||
"announce_peer to %v: %v",
|
||||
elem, a.announcePeer(elem),
|
||||
)
|
||||
wg.Done()
|
||||
}()
|
||||
})
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (a *Announce) announcePeer(peer dhtutil.Elem) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go func() {
|
||||
select {
|
||||
case <-a.closed.Done():
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
return a.server.announcePeer(
|
||||
ctx,
|
||||
NewAddr(peer.Addr.UDP()),
|
||||
a.infoHash,
|
||||
a.announcePeerOpts.Port,
|
||||
peer.Data.(string),
|
||||
a.announcePeerOpts.ImpliedPort,
|
||||
QueryRateLimiting{},
|
||||
).Err
|
||||
}
|
||||
|
||||
func (a *Announce) getPeers(ctx context.Context, addr krpc.NodeAddr) (tqr traversal.QueryResult) {
|
||||
res := a.server.GetPeers(ctx, NewAddr(addr.UDP()), a.infoHash, a.scrape, QueryRateLimiting{})
|
||||
if r := res.Reply.R; r != nil {
|
||||
peersValues := PeersValues{
|
||||
Peers: r.Values,
|
||||
NodeInfo: krpc.NodeInfo{
|
||||
Addr: addr,
|
||||
ID: r.ID,
|
||||
},
|
||||
Return: *r,
|
||||
}
|
||||
select {
|
||||
case a.Peers <- peersValues:
|
||||
case <-a.traversal.Stopped():
|
||||
}
|
||||
if r.Token != nil {
|
||||
tqr.ClosestData = *r.Token
|
||||
tqr.ResponseFrom = &krpc.NodeInfo{
|
||||
ID: r.ID,
|
||||
Addr: addr,
|
||||
}
|
||||
}
|
||||
tqr.Nodes = r.Nodes
|
||||
tqr.Nodes6 = r.Nodes6
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
krpc.Return
|
||||
}
|
||||
|
||||
// Stop the announce.
|
||||
func (a *Announce) Close() {
|
||||
a.StopTraversing()
|
||||
// This will prevent peer announces from proceeding.
|
||||
a.closed.Set()
|
||||
}
|
||||
|
||||
func (a *Announce) logger() log.Logger {
|
||||
return a.server.logger()
|
||||
}
|
||||
|
||||
// Halts traversal, but won't block peer announcing.
|
||||
func (a *Announce) StopTraversing() {
|
||||
a.traversal.Stop()
|
||||
}
|
||||
|
||||
// Traversal and peer announcing steps are done.
|
||||
func (a *Announce) Finished() events.Done {
|
||||
// This is the last step in an announce.
|
||||
return a.peerAnnounced.Done()
|
||||
}
|
||||
27
vendor/github.com/anacrolix/dht/v2/bep44/error.go
generated
vendored
Normal file
27
vendor/github.com/anacrolix/dht/v2/bep44/error.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package bep44
|
||||
|
||||
import "github.com/anacrolix/dht/v2/krpc"
|
||||
|
||||
var (
|
||||
ErrValueFieldTooBig = krpc.Error{
|
||||
Code: krpc.ErrorCodeMessageValueFieldTooBig,
|
||||
Msg: "message (v field) too big",
|
||||
}
|
||||
ErrInvalidSignature = krpc.Error{
|
||||
Code: krpc.ErrorCodeInvalidSignature,
|
||||
Msg: "invalid signature",
|
||||
}
|
||||
|
||||
ErrSaltFieldTooBig = krpc.Error{
|
||||
Code: krpc.ErrorCodeSaltFieldTooBig,
|
||||
Msg: "salt (salt field) too big",
|
||||
}
|
||||
ErrCasHashMismatched = krpc.Error{
|
||||
Code: krpc.ErrorCodeCasHashMismatched,
|
||||
Msg: "the CAS hash mismatched, re-read value and try again",
|
||||
}
|
||||
ErrSequenceNumberLessThanCurrent = krpc.Error{
|
||||
Code: krpc.ErrorCodeSequenceNumberLessThanCurrent,
|
||||
Msg: "sequence number less than current",
|
||||
}
|
||||
)
|
||||
177
vendor/github.com/anacrolix/dht/v2/bep44/item.go
generated
vendored
Normal file
177
vendor/github.com/anacrolix/dht/v2/bep44/item.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
var Empty32ByteArray [32]byte
|
||||
|
||||
type Item struct {
|
||||
// time when this object was added to storage
|
||||
created time.Time
|
||||
|
||||
// Value to be stored
|
||||
V interface{}
|
||||
|
||||
// 32 byte ed25519 public key
|
||||
K [32]byte
|
||||
Salt []byte
|
||||
Sig [64]byte
|
||||
Cas int64
|
||||
Seq int64
|
||||
}
|
||||
|
||||
func (i *Item) ToPut() Put {
|
||||
p := Put{
|
||||
V: i.V,
|
||||
Salt: i.Salt,
|
||||
Sig: i.Sig,
|
||||
Cas: i.Cas,
|
||||
Seq: i.Seq,
|
||||
}
|
||||
if i.K != Empty32ByteArray {
|
||||
p.K = &i.K
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// NewItem creates a new arbitrary DHT element. The distinction between storing mutable
|
||||
// and immutable items is the inclusion of a public key, a sequence number, signature
|
||||
// and an optional salt.
|
||||
//
|
||||
// cas parameter is short for compare and swap, it has similar semantics as CAS CPU
|
||||
// instructions. It is used to avoid race conditions when multiple nodes are writing
|
||||
// to the same slot in the DHT. It is optional. If present it specifies the sequence
|
||||
// number of the data blob being overwritten by the put.
|
||||
//
|
||||
// salt parameter is used to make possible several targets using the same private key.
|
||||
//
|
||||
// The optional seq field specifies that an item's value should only be sent if its
|
||||
// sequence number is greater than the given value.
|
||||
func NewItem(value interface{}, salt []byte, seq, cas int64, k ed25519.PrivateKey) (*Item, error) {
|
||||
v, err := bencode.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kk [32]byte
|
||||
var sig [64]byte
|
||||
|
||||
if k != nil {
|
||||
pk := []byte(k.Public().(ed25519.PublicKey))
|
||||
copy(kk[:], pk)
|
||||
copy(sig[:], Sign(k, salt, seq, v))
|
||||
}
|
||||
|
||||
return &Item{
|
||||
V: value,
|
||||
Salt: salt,
|
||||
Cas: cas,
|
||||
Seq: seq,
|
||||
|
||||
K: kk,
|
||||
Sig: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *Item) Target() Target {
|
||||
if i.IsMutable() {
|
||||
return sha1.Sum(append(i.K[:], i.Salt...))
|
||||
}
|
||||
|
||||
return sha1.Sum(bencode.MustMarshal(i.V))
|
||||
}
|
||||
|
||||
func (i *Item) Modify(value interface{}, k ed25519.PrivateKey) bool {
|
||||
if !i.IsMutable() {
|
||||
return false
|
||||
}
|
||||
|
||||
v, err := bencode.Marshal(value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
i.V = value
|
||||
i.Seq++
|
||||
var sig [64]byte
|
||||
copy(sig[:], Sign(k, i.Salt, i.Seq, v))
|
||||
i.Sig = sig
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Item) IsMutable() bool {
|
||||
return s.K != Empty32ByteArray
|
||||
}
|
||||
|
||||
func bufferToSign(salt, bv []byte, seq int64) []byte {
|
||||
var bts []byte
|
||||
if len(salt) != 0 {
|
||||
bts = append(bts, []byte("4:salt")...)
|
||||
x := bencode.MustMarshal(salt)
|
||||
bts = append(bts, x...)
|
||||
}
|
||||
bts = append(bts, []byte(fmt.Sprintf("3:seqi%de1:v", seq))...)
|
||||
bts = append(bts, bv...)
|
||||
return bts
|
||||
}
|
||||
|
||||
func Check(i *Item) error {
|
||||
bv, err := bencode.Marshal(i.V)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bv) > 1000 {
|
||||
return ErrValueFieldTooBig
|
||||
}
|
||||
|
||||
if !i.IsMutable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(i.Salt) > 64 {
|
||||
return ErrSaltFieldTooBig
|
||||
}
|
||||
|
||||
if !Verify(i.K[:], i.Salt, i.Seq, bv, i.Sig[:]) {
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckIncoming(stored, incoming *Item) error {
|
||||
// If the sequence number is equal, and the value is also the same,
|
||||
// the node SHOULD reset its timeout counter.
|
||||
if stored.Seq == incoming.Seq {
|
||||
if bytes.Equal(
|
||||
bencode.MustMarshal(stored.V),
|
||||
bencode.MustMarshal(incoming.V),
|
||||
) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if stored.Seq >= incoming.Seq {
|
||||
return ErrSequenceNumberLessThanCurrent
|
||||
}
|
||||
|
||||
// Cas should be ignored if not present
|
||||
if stored.Cas == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if stored.Cas != incoming.Cas {
|
||||
return ErrCasHashMismatched
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
13
vendor/github.com/anacrolix/dht/v2/bep44/key.go
generated
vendored
Normal file
13
vendor/github.com/anacrolix/dht/v2/bep44/key.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
)
|
||||
|
||||
func Sign(k ed25519.PrivateKey, salt []byte, seq int64, bv []byte) []byte {
|
||||
return ed25519.Sign(k, bufferToSign(salt, bv, seq))
|
||||
}
|
||||
|
||||
func Verify(k ed25519.PublicKey, salt []byte, seq int64, bv []byte, sig []byte) bool {
|
||||
return ed25519.Verify(k, bufferToSign(salt, bv, seq), sig)
|
||||
}
|
||||
49
vendor/github.com/anacrolix/dht/v2/bep44/memory.go
generated
vendored
Normal file
49
vendor/github.com/anacrolix/dht/v2/bep44/memory.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ Store = &Memory{}
|
||||
|
||||
type Memory struct {
|
||||
// protects m
|
||||
mu sync.RWMutex
|
||||
m map[Target]*Item
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{
|
||||
m: make(map[Target]*Item),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Memory) Put(i *Item) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.m[i.Target()] = i
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Memory) Get(t Target) (*Item, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
i, ok := m.m[t]
|
||||
if !ok {
|
||||
return nil, ErrItemNotFound
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Memory) Del(t Target) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
delete(m.m, t)
|
||||
|
||||
return nil
|
||||
}
|
||||
47
vendor/github.com/anacrolix/dht/v2/bep44/put.go
generated
vendored
Normal file
47
vendor/github.com/anacrolix/dht/v2/bep44/put.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
type Put struct {
|
||||
V interface{}
|
||||
K *[32]byte
|
||||
Salt []byte
|
||||
Sig [64]byte
|
||||
Cas int64
|
||||
Seq int64
|
||||
}
|
||||
|
||||
func (p *Put) ToItem() *Item {
|
||||
i := &Item{
|
||||
V: p.V,
|
||||
Salt: p.Salt,
|
||||
Sig: p.Sig,
|
||||
Cas: p.Cas,
|
||||
Seq: p.Seq,
|
||||
}
|
||||
if p.K != nil {
|
||||
i.K = *p.K
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (p *Put) Sign(k ed25519.PrivateKey) {
|
||||
copy(p.Sig[:], Sign(k, p.Salt, p.Seq, bencode.MustMarshal(p.V)))
|
||||
}
|
||||
|
||||
func (i *Put) Target() Target {
|
||||
if i.IsMutable() {
|
||||
return MakeMutableTarget(*i.K, i.Salt)
|
||||
} else {
|
||||
return sha1.Sum(bencode.MustMarshal(i.V))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Put) IsMutable() bool {
|
||||
return s.K != nil
|
||||
}
|
||||
65
vendor/github.com/anacrolix/dht/v2/bep44/store.go
generated
vendored
Normal file
65
vendor/github.com/anacrolix/dht/v2/bep44/store.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrItemNotFound = errors.New("item not found")
|
||||
|
||||
type Store interface {
|
||||
Put(*Item) error
|
||||
Get(Target) (*Item, error)
|
||||
Del(Target) error
|
||||
}
|
||||
|
||||
// Wrapper is in charge of validate all new items and
|
||||
// decide when to store, or ignore them depending of the BEP 44 definition.
|
||||
// It is also in charge of removing expired items.
|
||||
type Wrapper struct {
|
||||
s Store
|
||||
exp time.Duration
|
||||
}
|
||||
|
||||
func NewWrapper(s Store, exp time.Duration) *Wrapper {
|
||||
return &Wrapper{s: s, exp: exp}
|
||||
}
|
||||
|
||||
func (w *Wrapper) Put(i *Item) error {
|
||||
if err := Check(i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
is, err := w.s.Get(i.Target())
|
||||
if errors.Is(err, ErrItemNotFound) {
|
||||
i.created = time.Now().Local()
|
||||
return w.s.Put(i)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := CheckIncoming(is, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.created = time.Now().Local()
|
||||
return w.s.Put(i)
|
||||
}
|
||||
|
||||
func (w *Wrapper) Get(t Target) (*Item, error) {
|
||||
i, err := w.s.Get(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i.created.Add(w.exp).After(time.Now().Local()) {
|
||||
return i, nil
|
||||
}
|
||||
|
||||
if err := w.s.Del(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, ErrItemNotFound
|
||||
}
|
||||
11
vendor/github.com/anacrolix/dht/v2/bep44/target.go
generated
vendored
Normal file
11
vendor/github.com/anacrolix/dht/v2/bep44/target.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package bep44
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
)
|
||||
|
||||
type Target = [sha1.Size]byte
|
||||
|
||||
func MakeMutableTarget(pubKey [32]byte, salt []byte) Target {
|
||||
return sha1.Sum(append(pubKey[:], salt...))
|
||||
}
|
||||
51
vendor/github.com/anacrolix/dht/v2/bootstrap.go
generated
vendored
Normal file
51
vendor/github.com/anacrolix/dht/v2/bootstrap.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
"github.com/anacrolix/dht/v2/traversal"
|
||||
)
|
||||
|
||||
type TraversalStats = traversal.Stats
|
||||
|
||||
// Populates the node table.
|
||||
func (s *Server) Bootstrap() (_ TraversalStats, err error) {
|
||||
s.mu.Lock()
|
||||
if s.bootstrappingNow {
|
||||
s.mu.Unlock()
|
||||
err = errors.New("already bootstrapping")
|
||||
return
|
||||
}
|
||||
s.bootstrappingNow = true
|
||||
s.mu.Unlock()
|
||||
defer func() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.bootstrappingNow = false
|
||||
}()
|
||||
// Track number of responses, for STM use. (It's available via atomic in TraversalStats but that
|
||||
// won't let wake up STM transactions that are observing the value.)
|
||||
t := traversal.Start(traversal.OperationInput{
|
||||
Target: krpc.ID(s.id.AsByteArray()),
|
||||
K: 16,
|
||||
DoQuery: func(ctx context.Context, addr krpc.NodeAddr) traversal.QueryResult {
|
||||
return s.FindNode(NewAddr(addr.UDP()), s.id, QueryRateLimiting{}).TraversalQueryResult(addr)
|
||||
},
|
||||
NodeFilter: s.TraversalNodeFilter,
|
||||
})
|
||||
nodes, err := s.TraversalStartingNodes()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.AddNodes(nodes)
|
||||
s.mu.Lock()
|
||||
s.lastBootstrap = time.Now()
|
||||
s.mu.Unlock()
|
||||
<-t.Stalled()
|
||||
t.Stop()
|
||||
<-t.Stopped()
|
||||
return *t.Stats(), nil
|
||||
}
|
||||
50
vendor/github.com/anacrolix/dht/v2/bucket.go
generated
vendored
Normal file
50
vendor/github.com/anacrolix/dht/v2/bucket.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/chansync"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
)
|
||||
|
||||
type bucket struct {
|
||||
// Per the "Routing Table" section of BEP 5.
|
||||
changed chansync.BroadcastCond
|
||||
lastChanged time.Time
|
||||
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{}{}
|
||||
b.lastChanged = time.Now()
|
||||
b.changed.Broadcast()
|
||||
}
|
||||
|
||||
func (b *bucket) GetNode(addr Addr, id int160.T) *node {
|
||||
for n := range b.nodes {
|
||||
if n.hasAddrAndID(addr, id) {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
44
vendor/github.com/anacrolix/dht/v2/containers/addr-maybe-ids-by-distance.go
generated
vendored
Normal file
44
vendor/github.com/anacrolix/dht/v2/containers/addr-maybe-ids-by-distance.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package containers
|
||||
|
||||
import (
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/types"
|
||||
"github.com/anacrolix/missinggo/v2/iter"
|
||||
"github.com/anacrolix/stm/stmutil"
|
||||
)
|
||||
|
||||
type addrMaybeId = types.AddrMaybeId
|
||||
|
||||
type AddrMaybeIdsByDistance interface {
|
||||
Add(addrMaybeId) AddrMaybeIdsByDistance
|
||||
Next() addrMaybeId
|
||||
Delete(addrMaybeId) AddrMaybeIdsByDistance
|
||||
Len() int
|
||||
}
|
||||
|
||||
type stmSettishWrapper struct {
|
||||
set stmutil.Settish
|
||||
}
|
||||
|
||||
func (me stmSettishWrapper) Next() addrMaybeId {
|
||||
first, _ := iter.First(me.set.Iter)
|
||||
return first.(addrMaybeId)
|
||||
}
|
||||
|
||||
func (me stmSettishWrapper) Delete(x addrMaybeId) AddrMaybeIdsByDistance {
|
||||
return stmSettishWrapper{me.set.Delete(x)}
|
||||
}
|
||||
|
||||
func (me stmSettishWrapper) Len() int {
|
||||
return me.set.Len()
|
||||
}
|
||||
|
||||
func (me stmSettishWrapper) Add(x addrMaybeId) AddrMaybeIdsByDistance {
|
||||
return stmSettishWrapper{me.set.Add(x)}
|
||||
}
|
||||
|
||||
func NewImmutableAddrMaybeIdsByDistance(target int160.T) AddrMaybeIdsByDistance {
|
||||
return stmSettishWrapper{stmutil.NewSortedSet(func(l, r interface{}) bool {
|
||||
return l.(addrMaybeId).CloserThan(r.(addrMaybeId), target)
|
||||
})}
|
||||
}
|
||||
153
vendor/github.com/anacrolix/dht/v2/dht.go
generated
vendored
Normal file
153
vendor/github.com/anacrolix/dht/v2/dht.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
_ "crypto/sha1"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/dht/v2/bep44"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
peer_store "github.com/anacrolix/dht/v2/peer-store"
|
||||
"github.com/anacrolix/log"
|
||||
"github.com/anacrolix/missinggo/v2"
|
||||
"github.com/anacrolix/torrent/iplist"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
func defaultQueryResendDelay() time.Duration {
|
||||
// This should be the highest reasonable UDP latency an end-user might have.
|
||||
return 2 * 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 setting 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 krpc.ID
|
||||
Conn net.PacketConn
|
||||
// Don't respond to queries from other nodes.
|
||||
Passive bool
|
||||
// Whether to wait for rate limiting to allow us to reply.
|
||||
WaitToReply 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, ip net.IP, port int, portOk bool)
|
||||
// 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.
|
||||
PeerStore peer_store.Interface
|
||||
// BEP-44: Storing arbitrary data in the DHT. If not store provided, a default in-memory
|
||||
// implementation will be used.
|
||||
Store bep44.Store
|
||||
// BEP-44: expiration time with non-announced items. Two hours by default
|
||||
Exp time.Duration
|
||||
|
||||
// If no Logger is provided, log.Default is used and log.Debug messages are filtered out. Note
|
||||
// that all messages without a log.Level, have log.Debug added to them before being passed to
|
||||
// this Logger.
|
||||
Logger log.Logger
|
||||
|
||||
DefaultWant []krpc.Want
|
||||
|
||||
SendLimiter *rate.Limiter
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
var DefaultGlobalBootstrapHostPorts = []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
|
||||
"dht.anacrolix.link:42069",
|
||||
"router.bittorrent.cloud:42069",
|
||||
}
|
||||
|
||||
func GlobalBootstrapAddrs(network string) (addrs []Addr, err error) {
|
||||
initDnsResolver()
|
||||
for _, s := range DefaultGlobalBootstrapHostPorts {
|
||||
host, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hostAddrs, err := dnsResolver.LookupHost(context.Background(), host)
|
||||
if err != nil {
|
||||
// log.Default.WithDefaultLevel(log.Debug).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
|
||||
}
|
||||
|
||||
// Deprecated: Use function from krpc.
|
||||
func RandomNodeID() (id krpc.ID) {
|
||||
return krpc.RandomNodeID()
|
||||
}
|
||||
|
||||
func MakeDeterministicNodeID(public net.Addr) (id krpc.ID) {
|
||||
h := crypto.SHA1.New()
|
||||
h.Write([]byte(public.String()))
|
||||
h.Sum(id[:0:20])
|
||||
SecureNodeId(&id, missinggo.AddrIP(public))
|
||||
return
|
||||
}
|
||||
31
vendor/github.com/anacrolix/dht/v2/dns.go
generated
vendored
Normal file
31
vendor/github.com/anacrolix/dht/v2/dns.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/dnscache"
|
||||
)
|
||||
|
||||
var (
|
||||
// A cache to prevent wasteful/excessive use of DNS when trying to bootstrap.
|
||||
dnsResolver *dnscache.Resolver
|
||||
dnsResolverInit sync.Once
|
||||
)
|
||||
|
||||
func dnsResolverRefresher() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
<-ticker.C
|
||||
dnsResolver.Refresh(false)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/anacrolix/dht/issues/43
|
||||
func initDnsResolver() {
|
||||
dnsResolverInit.Do(func() {
|
||||
dnsResolver = &dnscache.Resolver{}
|
||||
go dnsResolverRefresher()
|
||||
})
|
||||
}
|
||||
22
vendor/github.com/anacrolix/dht/v2/doc.go
generated
vendored
Normal file
22
vendor/github.com/anacrolix/dht/v2/doc.go
generated
vendored
Normal 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
|
||||
16
vendor/github.com/anacrolix/dht/v2/expvar.go
generated
vendored
Normal file
16
vendor/github.com/anacrolix/dht/v2/expvar.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
)
|
||||
|
||||
var (
|
||||
readZeroPort = expvar.NewInt("dhtReadZeroPort")
|
||||
readBlocked = expvar.NewInt("dhtReadBlocked")
|
||||
readNotKRPCDict = expvar.NewInt("dhtReadNotKRPCDict")
|
||||
readUnmarshalError = expvar.NewInt("dhtReadUnmarshalError")
|
||||
announceErrors = expvar.NewInt("dhtAnnounceErrors")
|
||||
writeErrors = expvar.NewInt("dhtWriteErrors")
|
||||
writes = expvar.NewInt("dhtWrites")
|
||||
expvars = expvar.NewMap("dht")
|
||||
)
|
||||
7
vendor/github.com/anacrolix/dht/v2/globals.go
generated
vendored
Normal file
7
vendor/github.com/anacrolix/dht/v2/globals.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var DefaultSendLimiter = rate.NewLimiter(25, 25)
|
||||
110
vendor/github.com/anacrolix/dht/v2/int160/int160.go
generated
vendored
Normal file
110
vendor/github.com/anacrolix/dht/v2/int160/int160.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package int160
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type T struct {
|
||||
bits [20]uint8
|
||||
}
|
||||
|
||||
func (me T) String() string {
|
||||
return hex.EncodeToString(me.bits[:])
|
||||
}
|
||||
|
||||
func (me *T) AsByteArray() [20]byte {
|
||||
return me.bits
|
||||
}
|
||||
|
||||
func (me *T) ByteString() string {
|
||||
return string(me.bits[:])
|
||||
}
|
||||
|
||||
func (me *T) BitLen() int {
|
||||
var a big.Int
|
||||
a.SetBytes(me.bits[:])
|
||||
return a.BitLen()
|
||||
}
|
||||
|
||||
func (me *T) SetBytes(b []byte) {
|
||||
n := copy(me.bits[:], b)
|
||||
if n != 20 {
|
||||
panic(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *T) SetBit(index int, val bool) {
|
||||
var orVal uint8
|
||||
if val {
|
||||
orVal = 1 << (7 - index%8)
|
||||
}
|
||||
var mask uint8 = ^(1 << (7 - index%8))
|
||||
me.bits[index/8] = me.bits[index/8]&mask | orVal
|
||||
}
|
||||
|
||||
func (me *T) GetBit(index int) bool {
|
||||
return me.bits[index/8]>>(7-index%8)&1 == 1
|
||||
}
|
||||
|
||||
func (me T) Bytes() []byte {
|
||||
return me.bits[:]
|
||||
}
|
||||
|
||||
func (l T) Cmp(r T) 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 *T) SetMax() {
|
||||
for i := range me.bits {
|
||||
me.bits[i] = math.MaxUint8
|
||||
}
|
||||
}
|
||||
|
||||
func (me *T) Xor(a, b *T) {
|
||||
for i := range me.bits {
|
||||
me.bits[i] = a.bits[i] ^ b.bits[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (me *T) IsZero() bool {
|
||||
for _, b := range me.bits {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func FromBytes(b []byte) (ret T) {
|
||||
ret.SetBytes(b)
|
||||
return
|
||||
}
|
||||
|
||||
func FromByteArray(b [20]byte) (ret T) {
|
||||
ret.SetBytes(b[:])
|
||||
return
|
||||
}
|
||||
|
||||
func FromByteString(s string) (ret T) {
|
||||
ret.SetBytes([]byte(s))
|
||||
return
|
||||
}
|
||||
|
||||
func Distance(a, b T) (ret T) {
|
||||
ret.Xor(&a, &b)
|
||||
return
|
||||
}
|
||||
|
||||
func (a T) Distance(b T) (ret T) {
|
||||
ret.Xor(&a, &b)
|
||||
return
|
||||
}
|
||||
103
vendor/github.com/anacrolix/dht/v2/k-nearest-nodes/k-nearest-nodes.go.go
generated
vendored
Normal file
103
vendor/github.com/anacrolix/dht/v2/k-nearest-nodes/k-nearest-nodes.go.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
package k_nearest_nodes
|
||||
|
||||
import (
|
||||
"hash/maphash"
|
||||
|
||||
"github.com/anacrolix/multiless"
|
||||
"github.com/benbjohnson/immutable"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
type Key = krpc.NodeInfo
|
||||
|
||||
type Elem struct {
|
||||
Key
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type Type struct {
|
||||
inner *immutable.SortedMap
|
||||
k int
|
||||
}
|
||||
|
||||
func New(target int160.T, k int) Type {
|
||||
seed := maphash.MakeSeed()
|
||||
return Type{
|
||||
k: k,
|
||||
inner: immutable.NewSortedMap(lessComparer{less: func(_l, _r interface{}) bool {
|
||||
l := _l.(Key)
|
||||
r := _r.(Key)
|
||||
return multiless.New().Cmp(
|
||||
l.ID.Int160().Distance(target).Cmp(r.ID.Int160().Distance(target)),
|
||||
).Lazy(func() multiless.Computation {
|
||||
var lh, rh maphash.Hash
|
||||
lh.SetSeed(seed)
|
||||
rh.SetSeed(seed)
|
||||
lh.WriteString(l.Addr.String())
|
||||
rh.WriteString(r.Addr.String())
|
||||
return multiless.New().Int64(int64(lh.Sum64()), int64(rh.Sum64()))
|
||||
}).Less()
|
||||
}}),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Type) Range(f func(Elem)) {
|
||||
iter := me.inner.Iterator()
|
||||
for !iter.Done() {
|
||||
key, value := iter.Next()
|
||||
f(Elem{
|
||||
Key: key.(Key),
|
||||
Data: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (me Type) Len() int {
|
||||
return me.inner.Len()
|
||||
}
|
||||
|
||||
func (me Type) Push(elem Elem) Type {
|
||||
me.inner = me.inner.Set(elem.Key, elem.Data)
|
||||
for me.inner.Len() > me.k {
|
||||
iter := me.inner.Iterator()
|
||||
iter.Last()
|
||||
key, _ := iter.Next()
|
||||
me.inner = me.inner.Delete(key)
|
||||
}
|
||||
return me
|
||||
}
|
||||
|
||||
func (me Type) Farthest() (elem Elem) {
|
||||
iter := me.inner.Iterator()
|
||||
iter.Last()
|
||||
if iter.Done() {
|
||||
panic(me.k)
|
||||
}
|
||||
key, value := iter.Next()
|
||||
return Elem{
|
||||
Key: key.(Key),
|
||||
Data: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (me Type) Full() bool {
|
||||
return me.Len() >= me.k
|
||||
}
|
||||
|
||||
type lessFunc func(l, r interface{}) bool
|
||||
|
||||
type lessComparer struct {
|
||||
less lessFunc
|
||||
}
|
||||
|
||||
func (me lessComparer) Compare(i, j interface{}) int {
|
||||
if me.less(i, j) {
|
||||
return -1
|
||||
} else if me.less(j, i) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
36
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv4NodeAddrs.go
generated
vendored
Normal file
36
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv4NodeAddrs.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package krpc
|
||||
|
||||
import "github.com/anacrolix/missinggo/slices"
|
||||
|
||||
type CompactIPv4NodeAddrs []NodeAddr
|
||||
|
||||
func (CompactIPv4NodeAddrs) ElemSize() int { return 6 }
|
||||
|
||||
func (me CompactIPv4NodeAddrs) MarshalBinary() ([]byte, error) {
|
||||
return marshalBinarySlice(slices.Map(func(addr NodeAddr) NodeAddr {
|
||||
if a := addr.IP.To4(); a != nil {
|
||||
addr.IP = a
|
||||
}
|
||||
return addr
|
||||
}, me).(CompactIPv4NodeAddrs))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (me CompactIPv4NodeAddrs) Index(x NodeAddr) int {
|
||||
return addrIndex(me, x)
|
||||
}
|
||||
37
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv4NodeInfo.go
generated
vendored
Normal file
37
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv4NodeInfo.go
generated
vendored
Normal 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)
|
||||
}
|
||||
34
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv6NodeAddrs.go
generated
vendored
Normal file
34
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv6NodeAddrs.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
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
|
||||
}
|
||||
|
||||
func (me CompactIPv6NodeAddrs) Index(x NodeAddr) int {
|
||||
return addrIndex(me, x)
|
||||
}
|
||||
32
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv6NodeInfo.go
generated
vendored
Normal file
32
vendor/github.com/anacrolix/dht/v2/krpc/CompactIPv6NodeInfo.go
generated
vendored
Normal 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)
|
||||
}
|
||||
49
vendor/github.com/anacrolix/dht/v2/krpc/bep33.go
generated
vendored
Normal file
49
vendor/github.com/anacrolix/dht/v2/krpc/bep33.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package krpc
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"math"
|
||||
"math/bits"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
m = 256 * 8
|
||||
k = 2
|
||||
)
|
||||
|
||||
type ScrapeBloomFilter [256]byte
|
||||
|
||||
// Note that if you intend for an IP to be in the IPv4 space, you might want to trim it to 4 bytes
|
||||
// with IP.To4.
|
||||
func (me *ScrapeBloomFilter) AddIp(ip net.IP) {
|
||||
h := sha1.New()
|
||||
h.Write(ip)
|
||||
var sum [20]byte
|
||||
h.Sum(sum[:0])
|
||||
me.addK(int(sum[0]) | int(sum[1])<<8)
|
||||
me.addK(int(sum[2]) | int(sum[3])<<8)
|
||||
}
|
||||
|
||||
func (me *ScrapeBloomFilter) addK(index int) {
|
||||
index %= m
|
||||
me[index/8] |= 1 << (index % 8)
|
||||
}
|
||||
|
||||
func (me ScrapeBloomFilter) countZeroes() (ret int) {
|
||||
for _, i := range me {
|
||||
ret += 8 - bits.OnesCount8(i)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *ScrapeBloomFilter) EstimateCount() float64 {
|
||||
if me == nil {
|
||||
return 0
|
||||
}
|
||||
c := float64(me.countZeroes())
|
||||
if c > m-1 {
|
||||
c = m - 1
|
||||
}
|
||||
return math.Log(c/m) / (k * math.Log(1.-1./m))
|
||||
}
|
||||
9
vendor/github.com/anacrolix/dht/v2/krpc/bep46.go
generated
vendored
Normal file
9
vendor/github.com/anacrolix/dht/v2/krpc/bep46.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package krpc
|
||||
|
||||
import (
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
type Bep46Payload struct {
|
||||
Ih metainfo.Hash `bencode:"ih"`
|
||||
}
|
||||
23
vendor/github.com/anacrolix/dht/v2/krpc/compact-infohashes.go
generated
vendored
Normal file
23
vendor/github.com/anacrolix/dht/v2/krpc/compact-infohashes.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package krpc
|
||||
|
||||
type Infohash [20]byte
|
||||
|
||||
type CompactInfohashes [][20]byte
|
||||
|
||||
func (CompactInfohashes) ElemSize() int { return 20 }
|
||||
|
||||
func (me CompactInfohashes) MarshalBinary() ([]byte, error) {
|
||||
return marshalBinarySlice(me)
|
||||
}
|
||||
|
||||
func (me CompactInfohashes) MarshalBencode() ([]byte, error) {
|
||||
return bencodeBytesResult(me.MarshalBinary())
|
||||
}
|
||||
|
||||
func (me *CompactInfohashes) UnmarshalBinary(b []byte) error {
|
||||
return unmarshalBinarySlice(me, b)
|
||||
}
|
||||
|
||||
func (me *CompactInfohashes) UnmarshalBencode(b []byte) error {
|
||||
return unmarshalBencodedBinary(me, b)
|
||||
}
|
||||
84
vendor/github.com/anacrolix/dht/v2/krpc/compact_helpers.go
generated
vendored
Normal file
84
vendor/github.com/anacrolix/dht/v2/krpc/compact_helpers.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
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()
|
||||
elem := reflect.New(elemType)
|
||||
for len(b) != 0 {
|
||||
if len(b) < bytesPerElem {
|
||||
err = fmt.Errorf("%d trailing bytes < %d required for element", len(b), bytesPerElem)
|
||||
break
|
||||
}
|
||||
if bu, ok := elem.Interface().(encoding.BinaryUnmarshaler); ok {
|
||||
err = bu.UnmarshalBinary(b[:bytesPerElem])
|
||||
} else if elem.Elem().Len() == bytesPerElem {
|
||||
reflect.Copy(elem.Elem(), reflect.ValueOf(b[:bytesPerElem]))
|
||||
} else {
|
||||
err = fmt.Errorf("can't unmarshal %v bytes into %v", bytesPerElem, elem.Type())
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// returns position of x in v, or -1 if not found
|
||||
func addrIndex(v []NodeAddr, x NodeAddr) int {
|
||||
for i := 0; i < len(v); i += 1 {
|
||||
if v[i].Equal(x) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
73
vendor/github.com/anacrolix/dht/v2/krpc/error.go
generated
vendored
Normal file
73
vendor/github.com/anacrolix/dht/v2/krpc/error.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package krpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
)
|
||||
|
||||
const (
|
||||
// These are documented in BEP 5.
|
||||
ErrorCodeGenericError = 201
|
||||
ErrorCodeServerError = 202
|
||||
ErrorCodeProtocolError = 203
|
||||
ErrorCodeMethodUnknown = 204
|
||||
// BEP 44
|
||||
ErrorCodeMessageValueFieldTooBig = 205
|
||||
ErrorCodeInvalidSignature = 206
|
||||
ErrorCodeSaltFieldTooBig = 207
|
||||
ErrorCodeCasHashMismatched = 301
|
||||
ErrorCodeSequenceNumberLessThanCurrent = 302
|
||||
)
|
||||
|
||||
var ErrorMethodUnknown = Error{
|
||||
Code: ErrorCodeMethodUnknown,
|
||||
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)
|
||||
}
|
||||
74
vendor/github.com/anacrolix/dht/v2/krpc/id.go
generated
vendored
Normal file
74
vendor/github.com/anacrolix/dht/v2/krpc/id.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package krpc
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"encoding"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
)
|
||||
|
||||
func RandomNodeID() (id ID) {
|
||||
crand.Read(id[:])
|
||||
return
|
||||
}
|
||||
|
||||
type ID [20]byte
|
||||
|
||||
var (
|
||||
_ interface {
|
||||
bencode.Unmarshaler
|
||||
encoding.TextUnmarshaler
|
||||
} = (*ID)(nil)
|
||||
_ bencode.Marshaler = ID{}
|
||||
_ fmt.Formatter = ID{}
|
||||
)
|
||||
|
||||
func (h ID) Format(f fmt.State, c rune) {
|
||||
// See metainfo.Hash.
|
||||
f.Write([]byte(h.String()))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (id *ID) UnmarshalText(b []byte) (err error) {
|
||||
n, err := hex.Decode(id[:], b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n != len(*id) {
|
||||
err = fmt.Errorf("expected %v bytes, only got %v", len(*id), n)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (id ID) String() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func (id ID) Int160() int160.T {
|
||||
return int160.FromByteArray(id)
|
||||
}
|
||||
131
vendor/github.com/anacrolix/dht/v2/krpc/msg.go
generated
vendored
Normal file
131
vendor/github.com/anacrolix/dht/v2/krpc/msg.go
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
package krpc
|
||||
|
||||
// Msg represents messages that nodes in the network send to each other as specified by the protocol.
|
||||
// They are also referred to as the KRPC messages.
|
||||
// There are three types of messages: QUERY, RESPONSE, ERROR
|
||||
// The message is a dictionary 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"` // BEP 43. Sender does not respond to queries.
|
||||
}
|
||||
|
||||
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 received from an earlier get_peers query. Also used in a BEP 44 put.
|
||||
Token string `bencode:"token,omitempty"`
|
||||
Port *int `bencode:"port,omitempty"` // Sender's 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.
|
||||
NoSeed int `bencode:"noseed,omitempty"` // BEP 33
|
||||
Scrape int `bencode:"scrape,omitempty"` // BEP 33
|
||||
|
||||
// BEP 44
|
||||
|
||||
// I don't know if we should use bencode.Bytes for this. If we unmarshalled bytes that didn't
|
||||
// marshal back the same, our hashes will not match. But this might also serve to prevent abuse.
|
||||
V interface{} `bencode:"v,omitempty"`
|
||||
Seq *int64 `bencode:"seq,omitempty"`
|
||||
Cas int64 `bencode:"cas,omitempty"`
|
||||
K [32]byte `bencode:"k,omitempty"`
|
||||
Salt []byte `bencode:"salt,omitempty"`
|
||||
Sig [64]byte `bencode:"sig,omitempty"`
|
||||
}
|
||||
|
||||
type Want string
|
||||
|
||||
const (
|
||||
WantNodes Want = "n4"
|
||||
WantNodes6 Want = "n6"
|
||||
)
|
||||
|
||||
// BEP 51 (DHT Infohash Indexing)
|
||||
type Bep51Return struct {
|
||||
Interval *int64 `bencode:"interval,omitempty"`
|
||||
Num *int64 `bencode:"num,omitempty"`
|
||||
// Nodes supporting this extension should always include the samples field in the response, even
|
||||
// when it is zero-length. This lets indexing nodes to distinguish nodes supporting this
|
||||
// extension from those that respond to unknown query types which contain a target field [2].
|
||||
Samples *CompactInfohashes `bencode:"samples,omitempty"`
|
||||
}
|
||||
|
||||
type Bep44Return struct {
|
||||
V interface{} `bencode:"v,omitempty"`
|
||||
K [32]byte `bencode:"k,omitempty"`
|
||||
Sig [64]byte `bencode:"sig,omitempty"`
|
||||
Seq *int64 `bencode:"seq,omitempty"`
|
||||
}
|
||||
|
||||
type Return struct {
|
||||
// All returns are supposed to contain an ID, but what if they don't?
|
||||
ID ID `bencode:"id"` // ID of the queried (and responding) node
|
||||
|
||||
// K closest nodes to the requested target. Included in responses to queries that imply
|
||||
// traversal, for example get_peers, find_nodes, get, sample_infohashes.
|
||||
Nodes CompactIPv4NodeInfo `bencode:"nodes,omitempty"`
|
||||
Nodes6 CompactIPv6NodeInfo `bencode:"nodes6,omitempty"`
|
||||
|
||||
Token *string `bencode:"token,omitempty"` // Token for future announce_peer or put (BEP 44)
|
||||
Values []NodeAddr `bencode:"values,omitempty"` // Torrent peers
|
||||
|
||||
// BEP 33 (scrapes)
|
||||
BFsd *ScrapeBloomFilter `bencode:"BFsd,omitempty"`
|
||||
BFpe *ScrapeBloomFilter `bencode:"BFpe,omitempty"`
|
||||
|
||||
Bep51Return
|
||||
|
||||
// BEP 44
|
||||
Bep44Return
|
||||
}
|
||||
|
||||
func (r Return) ForAllNodes(f func(NodeInfo)) {
|
||||
for _, n := range r.Nodes {
|
||||
f(n)
|
||||
}
|
||||
for _, n := range r.Nodes6 {
|
||||
f(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// This does not return an error, but (*Error)(nil) is still a non-nil error. You have been warned!
|
||||
// This language is evil.
|
||||
func (m Msg) Error() *Error {
|
||||
if m.Y != "e" {
|
||||
return nil
|
||||
}
|
||||
return m.E
|
||||
}
|
||||
66
vendor/github.com/anacrolix/dht/v2/krpc/nodeaddr.go
generated
vendored
Normal file
66
vendor/github.com/anacrolix/dht/v2/krpc/nodeaddr.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
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
|
||||
}
|
||||
|
||||
func (me NodeAddr) Equal(x NodeAddr) bool {
|
||||
return me.IP.Equal(x.IP) && me.Port == x.Port
|
||||
}
|
||||
46
vendor/github.com/anacrolix/dht/v2/krpc/nodeinfo.go
generated
vendored
Normal file
46
vendor/github.com/anacrolix/dht/v2/krpc/nodeinfo.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package krpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
)
|
||||
|
||||
type NodeInfo struct {
|
||||
ID ID
|
||||
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:])
|
||||
}
|
||||
36
vendor/github.com/anacrolix/dht/v2/misc.go
generated
vendored
Normal file
36
vendor/github.com/anacrolix/dht/v2/misc.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
"github.com/anacrolix/dht/v2/types"
|
||||
"github.com/anacrolix/missinggo/v2/iter"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
type addrMaybeId = types.AddrMaybeId
|
||||
|
||||
func randomIdInBucket(rootId int160.T, bucketIndex int) int160.T {
|
||||
id := int160.FromByteArray(krpc.RandomNodeID())
|
||||
for i := range iter.N(bucketIndex) {
|
||||
id.SetBit(i, rootId.GetBit(i))
|
||||
}
|
||||
id.SetBit(bucketIndex, !rootId.GetBit(bucketIndex))
|
||||
return id
|
||||
}
|
||||
1
vendor/github.com/anacrolix/dht/v2/nearest-nodes.go
generated
vendored
Normal file
1
vendor/github.com/anacrolix/dht/v2/nearest-nodes.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package dht
|
||||
56
vendor/github.com/anacrolix/dht/v2/node.go
generated
vendored
Normal file
56
vendor/github.com/anacrolix/dht/v2/node.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
type nodeKey struct {
|
||||
Addr Addr
|
||||
Id int160.T
|
||||
}
|
||||
|
||||
type node struct {
|
||||
nodeKey
|
||||
|
||||
lastGotQuery time.Time // From the remote node
|
||||
lastGotResponse time.Time // From the remote node
|
||||
|
||||
numReceivesFrom int
|
||||
failedLastQuestionablePing bool
|
||||
}
|
||||
|
||||
func (s *Server) IsQuestionable(n *node) bool {
|
||||
return !s.IsGood(n) && !s.nodeIsBad(n)
|
||||
}
|
||||
|
||||
func (n *node) hasAddrAndID(addr Addr, id int160.T) bool {
|
||||
return id == n.Id && n.Addr.String() == addr.String()
|
||||
}
|
||||
|
||||
func (n *node) IsSecure() bool {
|
||||
return NodeIdSecure(n.Id.AsByteArray(), n.Addr.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 (s *Server) IsGood(n *node) bool {
|
||||
if s.nodeIsBad(n) {
|
||||
return false
|
||||
}
|
||||
return time.Since(n.lastGotResponse) < 15*time.Minute ||
|
||||
!n.lastGotResponse.IsZero() && time.Since(n.lastGotQuery) < 15*time.Minute
|
||||
}
|
||||
43
vendor/github.com/anacrolix/dht/v2/nodes_file.go
generated
vendored
Normal file
43
vendor/github.com/anacrolix/dht/v2/nodes_file.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/anacrolix/dht/v2/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, 0o640)
|
||||
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
|
||||
}
|
||||
109
vendor/github.com/anacrolix/dht/v2/peer-store/in-memory.go
generated
vendored
Normal file
109
vendor/github.com/anacrolix/dht/v2/peer-store/in-memory.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package peer_store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
debug_writer "github.com/anacrolix/confluence/debug-writer"
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
"github.com/anacrolix/multiless"
|
||||
)
|
||||
|
||||
type InMemory struct {
|
||||
// This is used for sorting infohashes by distance in WriteDebug.
|
||||
RootId int160.T
|
||||
mu sync.RWMutex
|
||||
index map[InfoHash]indexValue
|
||||
}
|
||||
|
||||
// A uniqueness key for entries to the entry details
|
||||
type indexValue = map[string]NodeAndTime
|
||||
|
||||
var _ interface {
|
||||
debug_writer.Interface
|
||||
} = (*InMemory)(nil)
|
||||
|
||||
func (me *InMemory) GetPeers(ih InfoHash) (ret []krpc.NodeAddr) {
|
||||
me.mu.RLock()
|
||||
defer me.mu.RUnlock()
|
||||
for b := range me.index[ih] {
|
||||
var r krpc.NodeAddr
|
||||
err := r.UnmarshalBinary([]byte(b))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ret = append(ret, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *InMemory) AddPeer(ih InfoHash, na krpc.NodeAddr) {
|
||||
key := string(na.IP)
|
||||
me.mu.Lock()
|
||||
defer me.mu.Unlock()
|
||||
if me.index == nil {
|
||||
me.index = make(map[InfoHash]indexValue)
|
||||
}
|
||||
nodes := me.index[ih]
|
||||
if nodes == nil {
|
||||
nodes = make(indexValue)
|
||||
me.index[ih] = nodes
|
||||
}
|
||||
nodes[key] = NodeAndTime{na, time.Now()}
|
||||
}
|
||||
|
||||
type NodeAndTime struct {
|
||||
krpc.NodeAddr
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (me *InMemory) GetAll() (ret map[InfoHash][]NodeAndTime) {
|
||||
me.mu.RLock()
|
||||
defer me.mu.RUnlock()
|
||||
ret = make(map[InfoHash][]NodeAndTime, len(me.index))
|
||||
for ih, nodes := range me.index {
|
||||
for _, v := range nodes {
|
||||
ret[ih] = append(ret[ih], v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *InMemory) WriteDebug(w io.Writer) {
|
||||
all := me.GetAll()
|
||||
var totalCount int
|
||||
type sliceElem struct {
|
||||
InfoHash
|
||||
addrs []NodeAndTime
|
||||
}
|
||||
var allSlice []sliceElem
|
||||
for ih, addrs := range all {
|
||||
totalCount += len(addrs)
|
||||
allSlice = append(allSlice, sliceElem{ih, addrs})
|
||||
}
|
||||
fmt.Fprintf(w, "total count: %v\n\n", totalCount)
|
||||
sort.Slice(allSlice, func(i, j int) bool {
|
||||
return int160.Distance(int160.FromByteArray(allSlice[i].InfoHash), me.RootId).Cmp(
|
||||
int160.Distance(int160.FromByteArray(allSlice[j].InfoHash), me.RootId)) < 0
|
||||
})
|
||||
for _, elem := range allSlice {
|
||||
addrs := elem.addrs
|
||||
fmt.Fprintf(w, "%v (count %v):\n", elem.InfoHash, len(addrs))
|
||||
sort.Slice(addrs, func(i, j int) bool {
|
||||
return multiless.New().Cmp(
|
||||
bytes.Compare(addrs[i].IP, addrs[j].IP)).Int64(
|
||||
addrs[j].Time.UnixNano(), addrs[i].Time.UnixNano()).Int(
|
||||
addrs[i].Port, addrs[j].Port,
|
||||
).MustLess()
|
||||
})
|
||||
for _, na := range addrs {
|
||||
fmt.Fprintf(w, "\t%v (age: %v)\n", na.NodeAddr, time.Since(na.Time))
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
13
vendor/github.com/anacrolix/dht/v2/peer-store/peer-store.go
generated
vendored
Normal file
13
vendor/github.com/anacrolix/dht/v2/peer-store/peer-store.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package peer_store
|
||||
|
||||
import (
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
type InfoHash = metainfo.Hash
|
||||
|
||||
type Interface interface {
|
||||
AddPeer(InfoHash, krpc.NodeAddr)
|
||||
GetPeers(InfoHash) []krpc.NodeAddr
|
||||
}
|
||||
102
vendor/github.com/anacrolix/dht/v2/security.go
generated
vendored
Normal file
102
vendor/github.com/anacrolix/dht/v2/security.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
"net"
|
||||
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
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 *krpc.ID, 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
|
||||
}
|
||||
1459
vendor/github.com/anacrolix/dht/v2/server.go
generated
vendored
Normal file
1459
vendor/github.com/anacrolix/dht/v2/server.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
134
vendor/github.com/anacrolix/dht/v2/table.go
generated
vendored
Normal file
134
vendor/github.com/anacrolix/dht/v2/table.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
)
|
||||
|
||||
// Node table, with indexes on distance from root ID to bucket, and node addr.
|
||||
type table struct {
|
||||
rootID int160.T
|
||||
k int
|
||||
buckets [160]bucket
|
||||
addrs map[string]map[int160.T]struct{}
|
||||
}
|
||||
|
||||
func (tbl *table) K() int {
|
||||
return tbl.k
|
||||
}
|
||||
|
||||
func (tbl *table) randomIdForBucket(bucketIndex int) int160.T {
|
||||
randomId := randomIdInBucket(tbl.rootID, bucketIndex)
|
||||
if randomIdBucketIndex := tbl.bucketIndex(randomId); randomIdBucketIndex != bucketIndex {
|
||||
panic(fmt.Sprintf("bucket index for random id %v == %v not %v", randomId, randomIdBucketIndex, bucketIndex))
|
||||
}
|
||||
return randomId
|
||||
}
|
||||
|
||||
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.T) *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.T) int {
|
||||
if id == tbl.rootID {
|
||||
panic("nobody puts the root ID in a bucket")
|
||||
}
|
||||
var a int160.T
|
||||
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.T) *node {
|
||||
if id == tbl.rootID {
|
||||
return nil
|
||||
}
|
||||
return tbl.buckets[tbl.bucketIndex(id)].GetNode(addr, id)
|
||||
}
|
||||
|
||||
func (tbl *table) closestNodes(k int, target int160.T, 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.T]struct{}, 160*tbl.k)
|
||||
}
|
||||
as := n.Addr.String()
|
||||
if tbl.addrs[as] == nil {
|
||||
tbl.addrs[as] = make(map[int160.T]struct{}, 1)
|
||||
}
|
||||
tbl.addrs[as][n.Id] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
57
vendor/github.com/anacrolix/dht/v2/tokens.go
generated
vendored
Normal file
57
vendor/github.com/anacrolix/dht/v2/tokens.go
generated
vendored
Normal 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.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()
|
||||
}
|
||||
48
vendor/github.com/anacrolix/dht/v2/transaction.go
generated
vendored
Normal file
48
vendor/github.com/anacrolix/dht/v2/transaction.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
var TransactionTimeout = errors.New("transaction timed out")
|
||||
|
||||
// Transaction keeps track of a message exchange between nodes, such as a
|
||||
// query message and a response message.
|
||||
type Transaction struct {
|
||||
onResponse func(krpc.Msg)
|
||||
}
|
||||
|
||||
func (t *Transaction) handleResponse(m krpc.Msg) {
|
||||
t.onResponse(m)
|
||||
}
|
||||
|
||||
const defaultMaxQuerySends = 1
|
||||
|
||||
func transactionSender(
|
||||
ctx context.Context,
|
||||
send func() error,
|
||||
resendDelay func() time.Duration,
|
||||
maxSends int,
|
||||
) error {
|
||||
var delay time.Duration
|
||||
sends := 0
|
||||
for sends < maxSends {
|
||||
select {
|
||||
case <-time.After(delay):
|
||||
err := send()
|
||||
sends++
|
||||
if err != nil {
|
||||
return fmt.Errorf("send %d: %w", sends, err)
|
||||
}
|
||||
delay = resendDelay()
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
232
vendor/github.com/anacrolix/dht/v2/traversal/operation.go
generated
vendored
Normal file
232
vendor/github.com/anacrolix/dht/v2/traversal/operation.go
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
package traversal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/anacrolix/chansync/events"
|
||||
"github.com/anacrolix/dht/v2/containers"
|
||||
"github.com/anacrolix/sync"
|
||||
|
||||
"github.com/anacrolix/chansync"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
k_nearest_nodes "github.com/anacrolix/dht/v2/k-nearest-nodes"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
"github.com/anacrolix/dht/v2/types"
|
||||
)
|
||||
|
||||
type QueryResult struct {
|
||||
// A node that should be considered for a closest entry.
|
||||
ResponseFrom *krpc.NodeInfo
|
||||
// Data associated with a closest node.
|
||||
ClosestData interface{}
|
||||
Nodes []krpc.NodeInfo
|
||||
Nodes6 []krpc.NodeInfo
|
||||
}
|
||||
|
||||
type OperationInput struct {
|
||||
Target krpc.ID
|
||||
Alpha int
|
||||
K int
|
||||
DoQuery func(context.Context, krpc.NodeAddr) QueryResult
|
||||
NodeFilter func(types.AddrMaybeId) bool
|
||||
}
|
||||
|
||||
type defaultsAppliedOperationInput OperationInput
|
||||
|
||||
func Start(input OperationInput) *Operation {
|
||||
herp := defaultsAppliedOperationInput(input)
|
||||
if herp.Alpha == 0 {
|
||||
herp.Alpha = 3
|
||||
}
|
||||
if herp.K == 0 {
|
||||
herp.K = 8
|
||||
}
|
||||
if herp.NodeFilter == nil {
|
||||
herp.NodeFilter = func(types.AddrMaybeId) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
targetInt160 := herp.Target.Int160()
|
||||
op := &Operation{
|
||||
targetInt160: targetInt160,
|
||||
input: herp,
|
||||
queried: make(map[addrString]struct{}),
|
||||
closest: k_nearest_nodes.New(targetInt160, herp.K),
|
||||
unqueried: containers.NewImmutableAddrMaybeIdsByDistance(targetInt160),
|
||||
}
|
||||
go op.run()
|
||||
return op
|
||||
}
|
||||
|
||||
type addrString string
|
||||
|
||||
type Operation struct {
|
||||
stats Stats
|
||||
mu sync.Mutex
|
||||
unqueried containers.AddrMaybeIdsByDistance
|
||||
queried map[addrString]struct{}
|
||||
closest k_nearest_nodes.Type
|
||||
targetInt160 int160.T
|
||||
input defaultsAppliedOperationInput
|
||||
outstanding int
|
||||
cond chansync.BroadcastCond
|
||||
stalled chansync.LevelTrigger
|
||||
stopping chansync.SetOnce
|
||||
stopped chansync.SetOnce
|
||||
}
|
||||
|
||||
func (op *Operation) Stats() *Stats {
|
||||
return &op.stats
|
||||
}
|
||||
|
||||
func (op *Operation) Stop() {
|
||||
if op.stopping.Set() {
|
||||
go func() {
|
||||
defer op.stopped.Set()
|
||||
op.mu.Lock()
|
||||
defer op.mu.Unlock()
|
||||
for {
|
||||
if op.outstanding == 0 {
|
||||
break
|
||||
}
|
||||
cond := op.cond.Signaled()
|
||||
op.mu.Unlock()
|
||||
<-cond
|
||||
op.mu.Lock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (op *Operation) Stopped() events.Done {
|
||||
return op.stopped.Done()
|
||||
}
|
||||
|
||||
func (op *Operation) Stalled() events.Active {
|
||||
return op.stalled.Active()
|
||||
}
|
||||
|
||||
func (op *Operation) AddNodes(nodes []types.AddrMaybeId) (added int) {
|
||||
op.mu.Lock()
|
||||
defer op.mu.Unlock()
|
||||
before := op.unqueried.Len()
|
||||
for _, n := range nodes {
|
||||
if _, ok := op.queried[addrString(n.Addr.String())]; ok {
|
||||
continue
|
||||
}
|
||||
if !op.input.NodeFilter(n) {
|
||||
continue
|
||||
}
|
||||
op.unqueried = op.unqueried.Add(n)
|
||||
}
|
||||
op.cond.Broadcast()
|
||||
return op.unqueried.Len() - before
|
||||
}
|
||||
|
||||
func (op *Operation) markQueried(addr krpc.NodeAddr) {
|
||||
op.queried[addrString(addr.String())] = struct{}{}
|
||||
}
|
||||
|
||||
func (op *Operation) closestUnqueried() (ret types.AddrMaybeId) {
|
||||
return op.unqueried.Next()
|
||||
}
|
||||
|
||||
func (op *Operation) popClosestUnqueried() types.AddrMaybeId {
|
||||
ret := op.closestUnqueried()
|
||||
op.unqueried = op.unqueried.Delete(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (op *Operation) haveQuery() bool {
|
||||
if op.unqueried.Len() == 0 {
|
||||
return false
|
||||
}
|
||||
if !op.closest.Full() {
|
||||
return true
|
||||
}
|
||||
cu := op.closestUnqueried()
|
||||
if cu.Id == nil {
|
||||
return false
|
||||
}
|
||||
return cu.Id.Distance(op.targetInt160).Cmp(op.closest.Farthest().ID.Int160().Distance(op.targetInt160)) <= 0
|
||||
}
|
||||
|
||||
func (op *Operation) run() {
|
||||
defer close(op.stalled.Signal())
|
||||
op.mu.Lock()
|
||||
defer op.mu.Unlock()
|
||||
for {
|
||||
if op.stopping.IsSet() {
|
||||
return
|
||||
}
|
||||
for op.outstanding < op.input.Alpha && op.haveQuery() {
|
||||
op.startQuery()
|
||||
}
|
||||
var stalled events.Signal
|
||||
if (!op.haveQuery() || op.input.Alpha == 0) && op.outstanding == 0 {
|
||||
stalled = op.stalled.Signal()
|
||||
}
|
||||
queryCondSignaled := op.cond.Signaled()
|
||||
op.mu.Unlock()
|
||||
select {
|
||||
case stalled <- struct{}{}:
|
||||
case <-op.stopping.Done():
|
||||
case <-queryCondSignaled:
|
||||
}
|
||||
op.mu.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (op *Operation) addClosest(node krpc.NodeInfo, data interface{}) {
|
||||
var ami types.AddrMaybeId
|
||||
ami.FromNodeInfo(node)
|
||||
if !op.input.NodeFilter(ami) {
|
||||
return
|
||||
}
|
||||
op.closest = op.closest.Push(k_nearest_nodes.Elem{
|
||||
Key: node,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
func (op *Operation) Closest() *k_nearest_nodes.Type {
|
||||
return &op.closest
|
||||
}
|
||||
|
||||
func (op *Operation) startQuery() {
|
||||
a := op.popClosestUnqueried()
|
||||
op.markQueried(a.Addr)
|
||||
op.outstanding++
|
||||
go func() {
|
||||
defer func() {
|
||||
op.mu.Lock()
|
||||
defer op.mu.Unlock()
|
||||
op.outstanding--
|
||||
op.cond.Broadcast()
|
||||
}()
|
||||
// log.Printf("traversal querying %v", a)
|
||||
atomic.AddUint32(&op.stats.NumAddrsTried, 1)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-op.stopping.Done():
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
res := op.input.DoQuery(ctx, a.Addr)
|
||||
cancel()
|
||||
if res.ResponseFrom != nil {
|
||||
func() {
|
||||
op.mu.Lock()
|
||||
defer op.mu.Unlock()
|
||||
atomic.AddUint32(&op.stats.NumResponses, 1)
|
||||
op.addClosest(*res.ResponseFrom, res.ClosestData)
|
||||
}()
|
||||
}
|
||||
op.AddNodes(types.AddrMaybeIdSliceFromNodeInfoSlice(res.Nodes))
|
||||
op.AddNodes(types.AddrMaybeIdSliceFromNodeInfoSlice(res.Nodes6))
|
||||
}()
|
||||
}
|
||||
8
vendor/github.com/anacrolix/dht/v2/traversal/stats.go
generated
vendored
Normal file
8
vendor/github.com/anacrolix/dht/v2/traversal/stats.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package traversal
|
||||
|
||||
type Stats struct {
|
||||
// Count of (probably) distinct addresses we've sent traversal queries to. Accessed with atomic.
|
||||
NumAddrsTried uint32
|
||||
// Number of responses we received to queries related to this traversal. Accessed with atomic.
|
||||
NumResponses uint32
|
||||
}
|
||||
72
vendor/github.com/anacrolix/dht/v2/types/addr-maybe-id.go
generated
vendored
Normal file
72
vendor/github.com/anacrolix/dht/v2/types/addr-maybe-id.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
|
||||
"github.com/anacrolix/multiless"
|
||||
|
||||
"github.com/anacrolix/dht/v2/int160"
|
||||
"github.com/anacrolix/dht/v2/krpc"
|
||||
)
|
||||
|
||||
func AddrMaybeIdSliceFromNodeInfoSlice(nis []krpc.NodeInfo) (ret []AddrMaybeId) {
|
||||
ret = make([]AddrMaybeId, 0, len(nis))
|
||||
for _, ni := range nis {
|
||||
id := int160.FromByteArray(ni.ID)
|
||||
ret = append(ret, AddrMaybeId{
|
||||
Addr: ni.Addr,
|
||||
Id: &id,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type AddrMaybeId struct {
|
||||
Addr krpc.NodeAddr
|
||||
Id *int160.T
|
||||
}
|
||||
|
||||
func (me AddrMaybeId) TryIntoNodeInfo() *krpc.NodeInfo {
|
||||
if me.Id == nil {
|
||||
return nil
|
||||
}
|
||||
return &krpc.NodeInfo{
|
||||
ID: me.Id.AsByteArray(),
|
||||
Addr: me.Addr,
|
||||
}
|
||||
}
|
||||
|
||||
func (me *AddrMaybeId) FromNodeInfo(ni krpc.NodeInfo) {
|
||||
id := int160.FromByteArray(ni.ID)
|
||||
*me = AddrMaybeId{
|
||||
Addr: ni.Addr,
|
||||
Id: &id,
|
||||
}
|
||||
}
|
||||
|
||||
func (me AddrMaybeId) String() string {
|
||||
if me.Id == nil {
|
||||
return fmt.Sprintf("unknown id at %s", me.Addr)
|
||||
} else {
|
||||
return fmt.Sprintf("%v at %v", *me.Id, me.Addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (l AddrMaybeId) CloserThan(r AddrMaybeId, target int160.T) bool {
|
||||
ml := multiless.New().Bool(l.Id == nil, r.Id == nil)
|
||||
if l.Id != nil && r.Id != nil {
|
||||
ml = ml.Cmp(l.Id.Distance(target).Cmp(r.Id.Distance(target)))
|
||||
}
|
||||
if !ml.Ok() {
|
||||
// We could use maphash, but it wasn't much faster, and requires a seed. A seed would allow
|
||||
// us to prevent deterministic handling of addrs for different uses.
|
||||
hashString := func(s string) uint64 {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(s))
|
||||
return h.Sum64()
|
||||
}
|
||||
ml = ml.Uint64(hashString(l.Addr.String()), hashString(r.Addr.String()))
|
||||
}
|
||||
return ml.Less()
|
||||
}
|
||||
Reference in New Issue
Block a user