mirror of
https://github.com/muun/recovery.git
synced 2025-11-13 07:11:45 -05:00
Release v0.1.0
This commit is contained in:
37
vendor/github.com/btcsuite/btcd/connmgr/README.md
generated
vendored
Normal file
37
vendor/github.com/btcsuite/btcd/connmgr/README.md
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
connmgr
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/btcsuite/btcd)
|
||||
[](http://copyfree.org)
|
||||
[](http://godoc.org/github.com/btcsuite/btcd/connmgr)
|
||||
|
||||
Package connmgr implements a generic Bitcoin network connection manager.
|
||||
|
||||
## Overview
|
||||
|
||||
Connection Manager handles all the general connection concerns such as
|
||||
maintaining a set number of outbound connections, sourcing peers, banning,
|
||||
limiting max connections, tor lookup, etc.
|
||||
|
||||
The package provides a generic connection manager which is able to accept
|
||||
connection requests from a source or a set of given addresses, dial them and
|
||||
notify the caller on connections. The main intended use is to initialize a pool
|
||||
of active connections and maintain them to remain connected to the P2P network.
|
||||
|
||||
In addition the connection manager provides the following utilities:
|
||||
|
||||
- Notifications on connections or disconnections
|
||||
- Handle failures and retry new addresses from the source
|
||||
- Connect only to specified addresses
|
||||
- Permanent connections with increasing backoff retry timers
|
||||
- Disconnect or Remove an established connection
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/connmgr
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package connmgr is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
577
vendor/github.com/btcsuite/btcd/connmgr/connmanager.go
generated
vendored
Normal file
577
vendor/github.com/btcsuite/btcd/connmgr/connmanager.go
generated
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// maxFailedAttempts is the maximum number of successive failed connection
|
||||
// attempts after which network failure is assumed and new connections will
|
||||
// be delayed by the configured retry duration.
|
||||
const maxFailedAttempts = 25
|
||||
|
||||
var (
|
||||
//ErrDialNil is used to indicate that Dial cannot be nil in the configuration.
|
||||
ErrDialNil = errors.New("Config: Dial cannot be nil")
|
||||
|
||||
// maxRetryDuration is the max duration of time retrying of a persistent
|
||||
// connection is allowed to grow to. This is necessary since the retry
|
||||
// logic uses a backoff mechanism which increases the interval base times
|
||||
// the number of retries that have been done.
|
||||
maxRetryDuration = time.Minute * 5
|
||||
|
||||
// defaultRetryDuration is the default duration of time for retrying
|
||||
// persistent connections.
|
||||
defaultRetryDuration = time.Second * 5
|
||||
|
||||
// defaultTargetOutbound is the default number of outbound connections to
|
||||
// maintain.
|
||||
defaultTargetOutbound = uint32(8)
|
||||
)
|
||||
|
||||
// ConnState represents the state of the requested connection.
|
||||
type ConnState uint8
|
||||
|
||||
// ConnState can be either pending, established, disconnected or failed. When
|
||||
// a new connection is requested, it is attempted and categorized as
|
||||
// established or failed depending on the connection result. An established
|
||||
// connection which was disconnected is categorized as disconnected.
|
||||
const (
|
||||
ConnPending ConnState = iota
|
||||
ConnFailing
|
||||
ConnCanceled
|
||||
ConnEstablished
|
||||
ConnDisconnected
|
||||
)
|
||||
|
||||
// ConnReq is the connection request to a network address. If permanent, the
|
||||
// connection will be retried on disconnection.
|
||||
type ConnReq struct {
|
||||
// The following variables must only be used atomically.
|
||||
id uint64
|
||||
|
||||
Addr net.Addr
|
||||
Permanent bool
|
||||
|
||||
conn net.Conn
|
||||
state ConnState
|
||||
stateMtx sync.RWMutex
|
||||
retryCount uint32
|
||||
}
|
||||
|
||||
// updateState updates the state of the connection request.
|
||||
func (c *ConnReq) updateState(state ConnState) {
|
||||
c.stateMtx.Lock()
|
||||
c.state = state
|
||||
c.stateMtx.Unlock()
|
||||
}
|
||||
|
||||
// ID returns a unique identifier for the connection request.
|
||||
func (c *ConnReq) ID() uint64 {
|
||||
return atomic.LoadUint64(&c.id)
|
||||
}
|
||||
|
||||
// State is the connection state of the requested connection.
|
||||
func (c *ConnReq) State() ConnState {
|
||||
c.stateMtx.RLock()
|
||||
state := c.state
|
||||
c.stateMtx.RUnlock()
|
||||
return state
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the connection request.
|
||||
func (c *ConnReq) String() string {
|
||||
if c.Addr == nil || c.Addr.String() == "" {
|
||||
return fmt.Sprintf("reqid %d", atomic.LoadUint64(&c.id))
|
||||
}
|
||||
return fmt.Sprintf("%s (reqid %d)", c.Addr, atomic.LoadUint64(&c.id))
|
||||
}
|
||||
|
||||
// Config holds the configuration options related to the connection manager.
|
||||
type Config struct {
|
||||
// Listeners defines a slice of listeners for which the connection
|
||||
// manager will take ownership of and accept connections. When a
|
||||
// connection is accepted, the OnAccept handler will be invoked with the
|
||||
// connection. Since the connection manager takes ownership of these
|
||||
// listeners, they will be closed when the connection manager is
|
||||
// stopped.
|
||||
//
|
||||
// This field will not have any effect if the OnAccept field is not
|
||||
// also specified. It may be nil if the caller does not wish to listen
|
||||
// for incoming connections.
|
||||
Listeners []net.Listener
|
||||
|
||||
// OnAccept is a callback that is fired when an inbound connection is
|
||||
// accepted. It is the caller's responsibility to close the connection.
|
||||
// Failure to close the connection will result in the connection manager
|
||||
// believing the connection is still active and thus have undesirable
|
||||
// side effects such as still counting toward maximum connection limits.
|
||||
//
|
||||
// This field will not have any effect if the Listeners field is not
|
||||
// also specified since there couldn't possibly be any accepted
|
||||
// connections in that case.
|
||||
OnAccept func(net.Conn)
|
||||
|
||||
// TargetOutbound is the number of outbound network connections to
|
||||
// maintain. Defaults to 8.
|
||||
TargetOutbound uint32
|
||||
|
||||
// RetryDuration is the duration to wait before retrying connection
|
||||
// requests. Defaults to 5s.
|
||||
RetryDuration time.Duration
|
||||
|
||||
// OnConnection is a callback that is fired when a new outbound
|
||||
// connection is established.
|
||||
OnConnection func(*ConnReq, net.Conn)
|
||||
|
||||
// OnDisconnection is a callback that is fired when an outbound
|
||||
// connection is disconnected.
|
||||
OnDisconnection func(*ConnReq)
|
||||
|
||||
// GetNewAddress is a way to get an address to make a network connection
|
||||
// to. If nil, no new connections will be made automatically.
|
||||
GetNewAddress func() (net.Addr, error)
|
||||
|
||||
// Dial connects to the address on the named network. It cannot be nil.
|
||||
Dial func(net.Addr) (net.Conn, error)
|
||||
}
|
||||
|
||||
// registerPending is used to register a pending connection attempt. By
|
||||
// registering pending connection attempts we allow callers to cancel pending
|
||||
// connection attempts before their successful or in the case they're not
|
||||
// longer wanted.
|
||||
type registerPending struct {
|
||||
c *ConnReq
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// handleConnected is used to queue a successful connection.
|
||||
type handleConnected struct {
|
||||
c *ConnReq
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// handleDisconnected is used to remove a connection.
|
||||
type handleDisconnected struct {
|
||||
id uint64
|
||||
retry bool
|
||||
}
|
||||
|
||||
// handleFailed is used to remove a pending connection.
|
||||
type handleFailed struct {
|
||||
c *ConnReq
|
||||
err error
|
||||
}
|
||||
|
||||
// ConnManager provides a manager to handle network connections.
|
||||
type ConnManager struct {
|
||||
// The following variables must only be used atomically.
|
||||
connReqCount uint64
|
||||
start int32
|
||||
stop int32
|
||||
|
||||
cfg Config
|
||||
wg sync.WaitGroup
|
||||
failedAttempts uint64
|
||||
requests chan interface{}
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// handleFailedConn handles a connection failed due to a disconnect or any
|
||||
// other failure. If permanent, it retries the connection after the configured
|
||||
// retry duration. Otherwise, if required, it makes a new connection request.
|
||||
// After maxFailedConnectionAttempts new connections will be retried after the
|
||||
// configured retry duration.
|
||||
func (cm *ConnManager) handleFailedConn(c *ConnReq) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
if c.Permanent {
|
||||
c.retryCount++
|
||||
d := time.Duration(c.retryCount) * cm.cfg.RetryDuration
|
||||
if d > maxRetryDuration {
|
||||
d = maxRetryDuration
|
||||
}
|
||||
log.Debugf("Retrying connection to %v in %v", c, d)
|
||||
time.AfterFunc(d, func() {
|
||||
cm.Connect(c)
|
||||
})
|
||||
} else if cm.cfg.GetNewAddress != nil {
|
||||
cm.failedAttempts++
|
||||
if cm.failedAttempts >= maxFailedAttempts {
|
||||
log.Debugf("Max failed connection attempts reached: [%d] "+
|
||||
"-- retrying connection in: %v", maxFailedAttempts,
|
||||
cm.cfg.RetryDuration)
|
||||
time.AfterFunc(cm.cfg.RetryDuration, func() {
|
||||
cm.NewConnReq()
|
||||
})
|
||||
} else {
|
||||
go cm.NewConnReq()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connHandler handles all connection related requests. It must be run as a
|
||||
// goroutine.
|
||||
//
|
||||
// The connection handler makes sure that we maintain a pool of active outbound
|
||||
// connections so that we remain connected to the network. Connection requests
|
||||
// are processed and mapped by their assigned ids.
|
||||
func (cm *ConnManager) connHandler() {
|
||||
|
||||
var (
|
||||
// pending holds all registered conn requests that have yet to
|
||||
// succeed.
|
||||
pending = make(map[uint64]*ConnReq)
|
||||
|
||||
// conns represents the set of all actively connected peers.
|
||||
conns = make(map[uint64]*ConnReq, cm.cfg.TargetOutbound)
|
||||
)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case req := <-cm.requests:
|
||||
switch msg := req.(type) {
|
||||
|
||||
case registerPending:
|
||||
connReq := msg.c
|
||||
connReq.updateState(ConnPending)
|
||||
pending[msg.c.id] = connReq
|
||||
close(msg.done)
|
||||
|
||||
case handleConnected:
|
||||
connReq := msg.c
|
||||
|
||||
if _, ok := pending[connReq.id]; !ok {
|
||||
if msg.conn != nil {
|
||||
msg.conn.Close()
|
||||
}
|
||||
log.Debugf("Ignoring connection for "+
|
||||
"canceled connreq=%v", connReq)
|
||||
continue
|
||||
}
|
||||
|
||||
connReq.updateState(ConnEstablished)
|
||||
connReq.conn = msg.conn
|
||||
conns[connReq.id] = connReq
|
||||
log.Debugf("Connected to %v", connReq)
|
||||
connReq.retryCount = 0
|
||||
cm.failedAttempts = 0
|
||||
|
||||
delete(pending, connReq.id)
|
||||
|
||||
if cm.cfg.OnConnection != nil {
|
||||
go cm.cfg.OnConnection(connReq, msg.conn)
|
||||
}
|
||||
|
||||
case handleDisconnected:
|
||||
connReq, ok := conns[msg.id]
|
||||
if !ok {
|
||||
connReq, ok = pending[msg.id]
|
||||
if !ok {
|
||||
log.Errorf("Unknown connid=%d",
|
||||
msg.id)
|
||||
continue
|
||||
}
|
||||
|
||||
// Pending connection was found, remove
|
||||
// it from pending map if we should
|
||||
// ignore a later, successful
|
||||
// connection.
|
||||
connReq.updateState(ConnCanceled)
|
||||
log.Debugf("Canceling: %v", connReq)
|
||||
delete(pending, msg.id)
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
// An existing connection was located, mark as
|
||||
// disconnected and execute disconnection
|
||||
// callback.
|
||||
log.Debugf("Disconnected from %v", connReq)
|
||||
delete(conns, msg.id)
|
||||
|
||||
if connReq.conn != nil {
|
||||
connReq.conn.Close()
|
||||
}
|
||||
|
||||
if cm.cfg.OnDisconnection != nil {
|
||||
go cm.cfg.OnDisconnection(connReq)
|
||||
}
|
||||
|
||||
// All internal state has been cleaned up, if
|
||||
// this connection is being removed, we will
|
||||
// make no further attempts with this request.
|
||||
if !msg.retry {
|
||||
connReq.updateState(ConnDisconnected)
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, we will attempt a reconnection if
|
||||
// we do not have enough peers, or if this is a
|
||||
// persistent peer. The connection request is
|
||||
// re added to the pending map, so that
|
||||
// subsequent processing of connections and
|
||||
// failures do not ignore the request.
|
||||
if uint32(len(conns)) < cm.cfg.TargetOutbound ||
|
||||
connReq.Permanent {
|
||||
|
||||
connReq.updateState(ConnPending)
|
||||
log.Debugf("Reconnecting to %v",
|
||||
connReq)
|
||||
pending[msg.id] = connReq
|
||||
cm.handleFailedConn(connReq)
|
||||
}
|
||||
|
||||
case handleFailed:
|
||||
connReq := msg.c
|
||||
|
||||
if _, ok := pending[connReq.id]; !ok {
|
||||
log.Debugf("Ignoring connection for "+
|
||||
"canceled conn req: %v", connReq)
|
||||
continue
|
||||
}
|
||||
|
||||
connReq.updateState(ConnFailing)
|
||||
log.Debugf("Failed to connect to %v: %v",
|
||||
connReq, msg.err)
|
||||
cm.handleFailedConn(connReq)
|
||||
}
|
||||
|
||||
case <-cm.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
cm.wg.Done()
|
||||
log.Trace("Connection handler done")
|
||||
}
|
||||
|
||||
// NewConnReq creates a new connection request and connects to the
|
||||
// corresponding address.
|
||||
func (cm *ConnManager) NewConnReq() {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
if cm.cfg.GetNewAddress == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := &ConnReq{}
|
||||
atomic.StoreUint64(&c.id, atomic.AddUint64(&cm.connReqCount, 1))
|
||||
|
||||
// Submit a request of a pending connection attempt to the connection
|
||||
// manager. By registering the id before the connection is even
|
||||
// established, we'll be able to later cancel the connection via the
|
||||
// Remove method.
|
||||
done := make(chan struct{})
|
||||
select {
|
||||
case cm.requests <- registerPending{c, done}:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the registration to successfully add the pending conn req to
|
||||
// the conn manager's internal state.
|
||||
select {
|
||||
case <-done:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := cm.cfg.GetNewAddress()
|
||||
if err != nil {
|
||||
select {
|
||||
case cm.requests <- handleFailed{c, err}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.Addr = addr
|
||||
|
||||
cm.Connect(c)
|
||||
}
|
||||
|
||||
// Connect assigns an id and dials a connection to the address of the
|
||||
// connection request.
|
||||
func (cm *ConnManager) Connect(c *ConnReq) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// During the time we wait for retry there is a chance that
|
||||
// this connection was already cancelled
|
||||
if c.State() == ConnCanceled {
|
||||
log.Debugf("Ignoring connect for canceled connreq=%v", c)
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadUint64(&c.id) == 0 {
|
||||
atomic.StoreUint64(&c.id, atomic.AddUint64(&cm.connReqCount, 1))
|
||||
|
||||
// Submit a request of a pending connection attempt to the
|
||||
// connection manager. By registering the id before the
|
||||
// connection is even established, we'll be able to later
|
||||
// cancel the connection via the Remove method.
|
||||
done := make(chan struct{})
|
||||
select {
|
||||
case cm.requests <- registerPending{c, done}:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the registration to successfully add the pending
|
||||
// conn req to the conn manager's internal state.
|
||||
select {
|
||||
case <-done:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Attempting to connect to %v", c)
|
||||
|
||||
conn, err := cm.cfg.Dial(c.Addr)
|
||||
if err != nil {
|
||||
select {
|
||||
case cm.requests <- handleFailed{c, err}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleConnected{c, conn}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect disconnects the connection corresponding to the given connection
|
||||
// id. If permanent, the connection will be retried with an increasing backoff
|
||||
// duration.
|
||||
func (cm *ConnManager) Disconnect(id uint64) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleDisconnected{id, true}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes the connection corresponding to the given connection id from
|
||||
// known connections.
|
||||
//
|
||||
// NOTE: This method can also be used to cancel a lingering connection attempt
|
||||
// that hasn't yet succeeded.
|
||||
func (cm *ConnManager) Remove(id uint64) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleDisconnected{id, false}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// listenHandler accepts incoming connections on a given listener. It must be
|
||||
// run as a goroutine.
|
||||
func (cm *ConnManager) listenHandler(listener net.Listener) {
|
||||
log.Infof("Server listening on %s", listener.Addr())
|
||||
for atomic.LoadInt32(&cm.stop) == 0 {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
// Only log the error if not forcibly shutting down.
|
||||
if atomic.LoadInt32(&cm.stop) == 0 {
|
||||
log.Errorf("Can't accept connection: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
go cm.cfg.OnAccept(conn)
|
||||
}
|
||||
|
||||
cm.wg.Done()
|
||||
log.Tracef("Listener handler done for %s", listener.Addr())
|
||||
}
|
||||
|
||||
// Start launches the connection manager and begins connecting to the network.
|
||||
func (cm *ConnManager) Start() {
|
||||
// Already started?
|
||||
if atomic.AddInt32(&cm.start, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Connection manager started")
|
||||
cm.wg.Add(1)
|
||||
go cm.connHandler()
|
||||
|
||||
// Start all the listeners so long as the caller requested them and
|
||||
// provided a callback to be invoked when connections are accepted.
|
||||
if cm.cfg.OnAccept != nil {
|
||||
for _, listner := range cm.cfg.Listeners {
|
||||
cm.wg.Add(1)
|
||||
go cm.listenHandler(listner)
|
||||
}
|
||||
}
|
||||
|
||||
for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ {
|
||||
go cm.NewConnReq()
|
||||
}
|
||||
}
|
||||
|
||||
// Wait blocks until the connection manager halts gracefully.
|
||||
func (cm *ConnManager) Wait() {
|
||||
cm.wg.Wait()
|
||||
}
|
||||
|
||||
// Stop gracefully shuts down the connection manager.
|
||||
func (cm *ConnManager) Stop() {
|
||||
if atomic.AddInt32(&cm.stop, 1) != 1 {
|
||||
log.Warnf("Connection manager already stopped")
|
||||
return
|
||||
}
|
||||
|
||||
// Stop all the listeners. There will not be any listeners if
|
||||
// listening is disabled.
|
||||
for _, listener := range cm.cfg.Listeners {
|
||||
// Ignore the error since this is shutdown and there is no way
|
||||
// to recover anyways.
|
||||
_ = listener.Close()
|
||||
}
|
||||
|
||||
close(cm.quit)
|
||||
log.Trace("Connection manager stopped")
|
||||
}
|
||||
|
||||
// New returns a new connection manager.
|
||||
// Use Start to start connecting to the network.
|
||||
func New(cfg *Config) (*ConnManager, error) {
|
||||
if cfg.Dial == nil {
|
||||
return nil, ErrDialNil
|
||||
}
|
||||
// Default to sane values
|
||||
if cfg.RetryDuration <= 0 {
|
||||
cfg.RetryDuration = defaultRetryDuration
|
||||
}
|
||||
if cfg.TargetOutbound == 0 {
|
||||
cfg.TargetOutbound = defaultTargetOutbound
|
||||
}
|
||||
cm := ConnManager{
|
||||
cfg: *cfg, // Copy so caller can't mutate
|
||||
requests: make(chan interface{}),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
return &cm, nil
|
||||
}
|
||||
14
vendor/github.com/btcsuite/btcd/connmgr/doc.go
generated
vendored
Normal file
14
vendor/github.com/btcsuite/btcd/connmgr/doc.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package connmgr implements a generic Bitcoin network connection manager.
|
||||
|
||||
Connection Manager Overview
|
||||
|
||||
Connection Manager handles all the general connection concerns such as
|
||||
maintaining a set number of outbound connections, sourcing peers, banning,
|
||||
limiting max connections, tor lookup, etc.
|
||||
*/
|
||||
package connmgr
|
||||
146
vendor/github.com/btcsuite/btcd/connmgr/dynamicbanscore.go
generated
vendored
Normal file
146
vendor/github.com/btcsuite/btcd/connmgr/dynamicbanscore.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Halflife defines the time (in seconds) by which the transient part
|
||||
// of the ban score decays to one half of it's original value.
|
||||
Halflife = 60
|
||||
|
||||
// lambda is the decaying constant.
|
||||
lambda = math.Ln2 / Halflife
|
||||
|
||||
// Lifetime defines the maximum age of the transient part of the ban
|
||||
// score to be considered a non-zero score (in seconds).
|
||||
Lifetime = 1800
|
||||
|
||||
// precomputedLen defines the amount of decay factors (one per second) that
|
||||
// should be precomputed at initialization.
|
||||
precomputedLen = 64
|
||||
)
|
||||
|
||||
// precomputedFactor stores precomputed exponential decay factors for the first
|
||||
// 'precomputedLen' seconds starting from t == 0.
|
||||
var precomputedFactor [precomputedLen]float64
|
||||
|
||||
// init precomputes decay factors.
|
||||
func init() {
|
||||
for i := range precomputedFactor {
|
||||
precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda)
|
||||
}
|
||||
}
|
||||
|
||||
// decayFactor returns the decay factor at t seconds, using precalculated values
|
||||
// if available, or calculating the factor if needed.
|
||||
func decayFactor(t int64) float64 {
|
||||
if t < precomputedLen {
|
||||
return precomputedFactor[t]
|
||||
}
|
||||
return math.Exp(-1.0 * float64(t) * lambda)
|
||||
}
|
||||
|
||||
// DynamicBanScore provides dynamic ban scores consisting of a persistent and a
|
||||
// decaying component. The persistent score could be utilized to create simple
|
||||
// additive banning policies similar to those found in other bitcoin node
|
||||
// implementations.
|
||||
//
|
||||
// The decaying score enables the creation of evasive logic which handles
|
||||
// misbehaving peers (especially application layer DoS attacks) gracefully
|
||||
// by disconnecting and banning peers attempting various kinds of flooding.
|
||||
// DynamicBanScore allows these two approaches to be used in tandem.
|
||||
//
|
||||
// Zero value: Values of type DynamicBanScore are immediately ready for use upon
|
||||
// declaration.
|
||||
type DynamicBanScore struct {
|
||||
lastUnix int64
|
||||
transient float64
|
||||
persistent uint32
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
// String returns the ban score as a human-readable string.
|
||||
func (s *DynamicBanScore) String() string {
|
||||
s.mtx.Lock()
|
||||
r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
|
||||
s.persistent, s.transient, s.lastUnix, s.Int())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Int returns the current ban score, the sum of the persistent and decaying
|
||||
// scores.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Int() uint32 {
|
||||
s.mtx.Lock()
|
||||
r := s.int(time.Now())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Increase increases both the persistent and decaying scores by the values
|
||||
// passed as parameters. The resulting score is returned.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 {
|
||||
s.mtx.Lock()
|
||||
r := s.increase(persistent, transient, time.Now())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Reset set both persistent and decaying scores to zero.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Reset() {
|
||||
s.mtx.Lock()
|
||||
s.persistent = 0
|
||||
s.transient = 0
|
||||
s.lastUnix = 0
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
// int returns the ban score, the sum of the persistent and decaying scores at a
|
||||
// given point in time.
|
||||
//
|
||||
// This function is not safe for concurrent access. It is intended to be used
|
||||
// internally and during testing.
|
||||
func (s *DynamicBanScore) int(t time.Time) uint32 {
|
||||
dt := t.Unix() - s.lastUnix
|
||||
if s.transient < 1 || dt < 0 || Lifetime < dt {
|
||||
return s.persistent
|
||||
}
|
||||
return s.persistent + uint32(s.transient*decayFactor(dt))
|
||||
}
|
||||
|
||||
// increase increases the persistent, the decaying or both scores by the values
|
||||
// passed as parameters. The resulting score is calculated as if the action was
|
||||
// carried out at the point time represented by the third parameter. The
|
||||
// resulting score is returned.
|
||||
//
|
||||
// This function is not safe for concurrent access.
|
||||
func (s *DynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 {
|
||||
s.persistent += persistent
|
||||
tu := t.Unix()
|
||||
dt := tu - s.lastUnix
|
||||
|
||||
if transient > 0 {
|
||||
if Lifetime < dt {
|
||||
s.transient = 0
|
||||
} else if s.transient > 1 && dt > 0 {
|
||||
s.transient *= decayFactor(dt)
|
||||
}
|
||||
s.transient += float64(transient)
|
||||
s.lastUnix = tu
|
||||
}
|
||||
return s.persistent + uint32(s.transient)
|
||||
}
|
||||
30
vendor/github.com/btcsuite/btcd/connmgr/log.go
generated
vendored
Normal file
30
vendor/github.com/btcsuite/btcd/connmgr/log.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import "github.com/btcsuite/btclog"
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
DisableLog()
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until either UseLogger or SetLogWriter are called.
|
||||
func DisableLog() {
|
||||
log = btclog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
75
vendor/github.com/btcsuite/btcd/connmgr/seed.go
generated
vendored
Normal file
75
vendor/github.com/btcsuite/btcd/connmgr/seed.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
// These constants are used by the DNS seed code to pick a random last
|
||||
// seen time.
|
||||
secondsIn3Days int32 = 24 * 60 * 60 * 3
|
||||
secondsIn4Days int32 = 24 * 60 * 60 * 4
|
||||
)
|
||||
|
||||
// OnSeed is the signature of the callback function which is invoked when DNS
|
||||
// seeding is succesfull.
|
||||
type OnSeed func(addrs []*wire.NetAddress)
|
||||
|
||||
// LookupFunc is the signature of the DNS lookup function.
|
||||
type LookupFunc func(string) ([]net.IP, error)
|
||||
|
||||
// SeedFromDNS uses DNS seeding to populate the address manager with peers.
|
||||
func SeedFromDNS(chainParams *chaincfg.Params, reqServices wire.ServiceFlag,
|
||||
lookupFn LookupFunc, seedFn OnSeed) {
|
||||
|
||||
for _, dnsseed := range chainParams.DNSSeeds {
|
||||
var host string
|
||||
if !dnsseed.HasFiltering || reqServices == wire.SFNodeNetwork {
|
||||
host = dnsseed.Host
|
||||
} else {
|
||||
host = fmt.Sprintf("x%x.%s", uint64(reqServices), dnsseed.Host)
|
||||
}
|
||||
|
||||
go func(host string) {
|
||||
randSource := mrand.New(mrand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
seedpeers, err := lookupFn(host)
|
||||
if err != nil {
|
||||
log.Infof("DNS discovery failed on seed %s: %v", host, err)
|
||||
return
|
||||
}
|
||||
numPeers := len(seedpeers)
|
||||
|
||||
log.Infof("%d addresses found from DNS seed %s", numPeers, host)
|
||||
|
||||
if numPeers == 0 {
|
||||
return
|
||||
}
|
||||
addresses := make([]*wire.NetAddress, len(seedpeers))
|
||||
// if this errors then we have *real* problems
|
||||
intPort, _ := strconv.Atoi(chainParams.DefaultPort)
|
||||
for i, peer := range seedpeers {
|
||||
addresses[i] = wire.NewNetAddressTimestamp(
|
||||
// bitcoind seeds with addresses from
|
||||
// a time randomly selected between 3
|
||||
// and 7 days ago.
|
||||
time.Now().Add(-1*time.Second*time.Duration(secondsIn3Days+
|
||||
randSource.Int31n(secondsIn4Days))),
|
||||
0, peer, uint16(intPort))
|
||||
}
|
||||
|
||||
seedFn(addresses)
|
||||
}(host)
|
||||
}
|
||||
}
|
||||
129
vendor/github.com/btcsuite/btcd/connmgr/tor.go
generated
vendored
Normal file
129
vendor/github.com/btcsuite/btcd/connmgr/tor.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
torSucceeded = 0x00
|
||||
torGeneralError = 0x01
|
||||
torNotAllowed = 0x02
|
||||
torNetUnreachable = 0x03
|
||||
torHostUnreachable = 0x04
|
||||
torConnectionRefused = 0x05
|
||||
torTTLExpired = 0x06
|
||||
torCmdNotSupported = 0x07
|
||||
torAddrNotSupported = 0x08
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTorInvalidAddressResponse indicates an invalid address was
|
||||
// returned by the Tor DNS resolver.
|
||||
ErrTorInvalidAddressResponse = errors.New("invalid address response")
|
||||
|
||||
// ErrTorInvalidProxyResponse indicates the Tor proxy returned a
|
||||
// response in an unexpected format.
|
||||
ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
|
||||
|
||||
// ErrTorUnrecognizedAuthMethod indicates the authentication method
|
||||
// provided is not recognized.
|
||||
ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method")
|
||||
|
||||
torStatusErrors = map[byte]error{
|
||||
torSucceeded: errors.New("tor succeeded"),
|
||||
torGeneralError: errors.New("tor general error"),
|
||||
torNotAllowed: errors.New("tor not allowed"),
|
||||
torNetUnreachable: errors.New("tor network is unreachable"),
|
||||
torHostUnreachable: errors.New("tor host is unreachable"),
|
||||
torConnectionRefused: errors.New("tor connection refused"),
|
||||
torTTLExpired: errors.New("tor TTL expired"),
|
||||
torCmdNotSupported: errors.New("tor command not supported"),
|
||||
torAddrNotSupported: errors.New("tor address type not supported"),
|
||||
}
|
||||
)
|
||||
|
||||
// TorLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for
|
||||
// resolution over the Tor network. Tor itself doesn't support ipv6 so this
|
||||
// doesn't either.
|
||||
func TorLookupIP(host, proxy string) ([]net.IP, error) {
|
||||
conn, err := net.Dial("tcp", proxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := []byte{'\x05', '\x01', '\x00'}
|
||||
_, err = conn.Write(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf = make([]byte, 2)
|
||||
_, err = conn.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[0] != '\x05' {
|
||||
return nil, ErrTorInvalidProxyResponse
|
||||
}
|
||||
if buf[1] != '\x00' {
|
||||
return nil, ErrTorUnrecognizedAuthMethod
|
||||
}
|
||||
|
||||
buf = make([]byte, 7+len(host))
|
||||
buf[0] = 5 // protocol version
|
||||
buf[1] = '\xF0' // Tor Resolve
|
||||
buf[2] = 0 // reserved
|
||||
buf[3] = 3 // Tor Resolve
|
||||
buf[4] = byte(len(host))
|
||||
copy(buf[5:], host)
|
||||
buf[5+len(host)] = 0 // Port 0
|
||||
|
||||
_, err = conn.Write(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf = make([]byte, 4)
|
||||
_, err = conn.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return nil, ErrTorInvalidProxyResponse
|
||||
}
|
||||
if buf[1] != 0 {
|
||||
if int(buf[1]) >= len(torStatusErrors) {
|
||||
return nil, ErrTorInvalidProxyResponse
|
||||
} else if err := torStatusErrors[buf[1]]; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, ErrTorInvalidProxyResponse
|
||||
}
|
||||
if buf[3] != 1 {
|
||||
err := torStatusErrors[torGeneralError]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf = make([]byte, 4)
|
||||
bytes, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes != 4 {
|
||||
return nil, ErrTorInvalidAddressResponse
|
||||
}
|
||||
|
||||
r := binary.BigEndian.Uint32(buf)
|
||||
|
||||
addr := make([]net.IP, 1)
|
||||
addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
Reference in New Issue
Block a user