mirror of
https://github.com/muun/recovery.git
synced 2025-11-10 22:10:14 -05:00
Release v0.1.0
This commit is contained in:
83
vendor/github.com/lightninglabs/neutrino/banman/codec.go
generated
vendored
Normal file
83
vendor/github.com/lightninglabs/neutrino/banman/codec.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package banman
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ipType represents the different types of IP addresses supported by the
|
||||
// BanStore interface.
|
||||
type ipType = byte
|
||||
|
||||
const (
|
||||
// ipv4 represents an IP address of type IPv4.
|
||||
ipv4 ipType = 0
|
||||
|
||||
// ipv6 represents an IP address of type IPv6.
|
||||
ipv6 ipType = 1
|
||||
)
|
||||
|
||||
// encodeIPNet serializes the IP network into the given reader.
|
||||
func encodeIPNet(w io.Writer, ipNet *net.IPNet) error {
|
||||
// Determine the appropriate IP type for the IP address contained in the
|
||||
// network.
|
||||
var (
|
||||
ip []byte
|
||||
ipType ipType
|
||||
)
|
||||
switch {
|
||||
case ipNet.IP.To4() != nil:
|
||||
ip = ipNet.IP.To4()
|
||||
ipType = ipv4
|
||||
case ipNet.IP.To16() != nil:
|
||||
ip = ipNet.IP.To16()
|
||||
ipType = ipv6
|
||||
default:
|
||||
return ErrUnsupportedIP
|
||||
}
|
||||
|
||||
// Write the IP type first in order to properly identify it when
|
||||
// deserializing it, followed by the IP itself and its mask.
|
||||
if _, err := w.Write([]byte{ipType}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(ip); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write([]byte(ipNet.Mask)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeIPNet deserialized an IP network from the given reader.
|
||||
func decodeIPNet(r io.Reader) (*net.IPNet, error) {
|
||||
// Read the IP address type and determine whether it is supported.
|
||||
var ipType [1]byte
|
||||
if _, err := r.Read(ipType[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ipLen int
|
||||
switch ipType[0] {
|
||||
case ipv4:
|
||||
ipLen = net.IPv4len
|
||||
case ipv6:
|
||||
ipLen = net.IPv6len
|
||||
default:
|
||||
return nil, ErrUnsupportedIP
|
||||
}
|
||||
|
||||
// Once we have the type and its corresponding length, attempt to read
|
||||
// it and its mask.
|
||||
ip := make([]byte, ipLen)
|
||||
if _, err := r.Read(ip[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mask := make([]byte, ipLen)
|
||||
if _, err := r.Read(mask[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &net.IPNet{IP: ip, Mask: mask}, nil
|
||||
}
|
||||
43
vendor/github.com/lightninglabs/neutrino/banman/reason.go
generated
vendored
Normal file
43
vendor/github.com/lightninglabs/neutrino/banman/reason.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package banman
|
||||
|
||||
// Reason includes the different possible reasons which caused us to ban a peer.
|
||||
type Reason uint8
|
||||
|
||||
// We prevent using `iota` to ensure the order does not have the value since
|
||||
// these are serialized within the database.
|
||||
const (
|
||||
// ExcedeedBanThreshold signals that a peer exceeded its ban threshold.
|
||||
ExceededBanThreshold Reason = 1
|
||||
|
||||
// NoCompactFilters signals that a peer was unable to serve us compact
|
||||
// filters.
|
||||
NoCompactFilters Reason = 2
|
||||
|
||||
// InvalidFilterHeader signals that a peer served us an invalid filter
|
||||
// header.
|
||||
InvalidFilterHeader Reason = 3
|
||||
|
||||
// InvalidFilterHeaderCheckpoint signals that a peer served us an
|
||||
// invalid filter header checkpoint.
|
||||
InvalidFilterHeaderCheckpoint Reason = 4
|
||||
)
|
||||
|
||||
// String returns a human-readable description for the reason a peer was banned.
|
||||
func (r Reason) String() string {
|
||||
switch r {
|
||||
case ExceededBanThreshold:
|
||||
return "peer exceeded ban threshold"
|
||||
|
||||
case NoCompactFilters:
|
||||
return "peer was unable to serve compact filters"
|
||||
|
||||
case InvalidFilterHeader:
|
||||
return "peer served invalid filter header"
|
||||
|
||||
case InvalidFilterHeaderCheckpoint:
|
||||
return "peer served invalid filter header checkpoint"
|
||||
|
||||
default:
|
||||
return "unknown reason"
|
||||
}
|
||||
}
|
||||
221
vendor/github.com/lightninglabs/neutrino/banman/store.go
generated
vendored
Normal file
221
vendor/github.com/lightninglabs/neutrino/banman/store.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
package banman
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
)
|
||||
|
||||
var (
|
||||
// byteOrder is the preferred byte order in which we should write things
|
||||
// to disk.
|
||||
byteOrder = binary.BigEndian
|
||||
|
||||
// banStoreBucket is the top level bucket of the Store that will contain
|
||||
// all relevant sub-buckets.
|
||||
banStoreBucket = []byte("ban-store")
|
||||
|
||||
// banBucket is the main index in which we keep track of IP networks and
|
||||
// their absolute expiration time.
|
||||
//
|
||||
// The key is the IP network host and the value is the absolute
|
||||
// expiration time.
|
||||
banBucket = []byte("ban-index")
|
||||
|
||||
// reasonBucket is an index in which we keep track of why an IP network
|
||||
// was banned.
|
||||
//
|
||||
// The key is the IP network and the value is the Reason.
|
||||
reasonBucket = []byte("reason-index")
|
||||
|
||||
// ErrCorruptedStore is an error returned when we attempt to locate any
|
||||
// of the ban-related buckets in the database but are unable to.
|
||||
ErrCorruptedStore = errors.New("corrupted ban store")
|
||||
|
||||
// ErrUnsupportedIP is an error returned when we attempt to parse an
|
||||
// unsupported IP address type.
|
||||
ErrUnsupportedIP = errors.New("unsupported IP type")
|
||||
)
|
||||
|
||||
// Status gathers all of the details regarding an IP network's ban status.
|
||||
type Status struct {
|
||||
// Banned determines whether the IP network is currently banned.
|
||||
Banned bool
|
||||
|
||||
// Reason is the reason for which the IP network was banned.
|
||||
Reason Reason
|
||||
|
||||
// Expiration is the absolute time in which the ban will expire.
|
||||
Expiration time.Time
|
||||
}
|
||||
|
||||
// Store is the store responsible for maintaining records of banned IP networks.
|
||||
// It uses IP networks, rather than single IP addresses, in order to coalesce
|
||||
// multiple IP addresses that are likely to be correlated.
|
||||
type Store interface {
|
||||
// BanIPNet creates a ban record for the IP network within the store for
|
||||
// the given duration. A reason can also be provided to note why the IP
|
||||
// network is being banned. The record will exist until a call to Status
|
||||
// is made after the ban expiration.
|
||||
BanIPNet(*net.IPNet, Reason, time.Duration) error
|
||||
|
||||
// Status returns the ban status for a given IP network.
|
||||
Status(*net.IPNet) (Status, error)
|
||||
}
|
||||
|
||||
// NewStore returns a Store backed by a database.
|
||||
func NewStore(db walletdb.DB) (Store, error) {
|
||||
return newBanStore(db)
|
||||
}
|
||||
|
||||
// banStore is a concrete implementation of the Store interface backed by a
|
||||
// database.
|
||||
type banStore struct {
|
||||
db walletdb.DB
|
||||
}
|
||||
|
||||
// A compile-time constraint to ensure banStore satisfies the Store interface.
|
||||
var _ Store = (*banStore)(nil)
|
||||
|
||||
// newBanStore creates a concrete implementation of the Store interface backed
|
||||
// by a database.
|
||||
func newBanStore(db walletdb.DB) (*banStore, error) {
|
||||
s := &banStore{db: db}
|
||||
|
||||
// We'll ensure the expected buckets are created upon initialization.
|
||||
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
|
||||
banStore, err := tx.CreateTopLevelBucket(banStoreBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = banStore.CreateBucketIfNotExists(banBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = banStore.CreateBucketIfNotExists(reasonBucket)
|
||||
return err
|
||||
})
|
||||
if err != nil && err != walletdb.ErrBucketExists {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// BanIPNet creates a ban record for the IP network within the store for the
|
||||
// given duration. A reason can also be provided to note why the IP network is
|
||||
// being banned. The record will exist until a call to Status is made after the
|
||||
// ban expiration.
|
||||
func (s *banStore) BanIPNet(ipNet *net.IPNet, reason Reason, duration time.Duration) error {
|
||||
return walletdb.Update(s.db, func(tx walletdb.ReadWriteTx) error {
|
||||
banStore := tx.ReadWriteBucket(banStoreBucket)
|
||||
if banStore == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
banIndex := banStore.NestedReadWriteBucket(banBucket)
|
||||
if banIndex == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
reasonIndex := banStore.NestedReadWriteBucket(reasonBucket)
|
||||
if reasonIndex == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
|
||||
var ipNetBuf bytes.Buffer
|
||||
if err := encodeIPNet(&ipNetBuf, ipNet); err != nil {
|
||||
return fmt.Errorf("unable to encode %v: %v", ipNet, err)
|
||||
}
|
||||
k := ipNetBuf.Bytes()
|
||||
|
||||
return addBannedIPNet(banIndex, reasonIndex, k, reason, duration)
|
||||
})
|
||||
}
|
||||
|
||||
// addBannedIPNet adds an entry to the ban store for the given IP network.
|
||||
func addBannedIPNet(banIndex, reasonIndex walletdb.ReadWriteBucket,
|
||||
ipNetKey []byte, reason Reason, duration time.Duration) error {
|
||||
|
||||
var v [8]byte
|
||||
banExpiration := time.Now().Add(duration)
|
||||
byteOrder.PutUint64(v[:], uint64(banExpiration.Unix()))
|
||||
|
||||
if err := banIndex.Put(ipNetKey, v[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
return reasonIndex.Put(ipNetKey, []byte{byte(reason)})
|
||||
}
|
||||
|
||||
// Status returns the ban status for a given IP network.
|
||||
func (s *banStore) Status(ipNet *net.IPNet) (Status, error) {
|
||||
var banStatus Status
|
||||
err := walletdb.Update(s.db, func(tx walletdb.ReadWriteTx) error {
|
||||
banStore := tx.ReadWriteBucket(banStoreBucket)
|
||||
if banStore == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
banIndex := banStore.NestedReadWriteBucket(banBucket)
|
||||
if banIndex == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
reasonIndex := banStore.NestedReadWriteBucket(reasonBucket)
|
||||
if reasonIndex == nil {
|
||||
return ErrCorruptedStore
|
||||
}
|
||||
|
||||
var ipNetBuf bytes.Buffer
|
||||
if err := encodeIPNet(&ipNetBuf, ipNet); err != nil {
|
||||
return fmt.Errorf("unable to encode %v: %v", ipNet, err)
|
||||
}
|
||||
k := ipNetBuf.Bytes()
|
||||
|
||||
status := fetchStatus(banIndex, reasonIndex, k)
|
||||
|
||||
// If the IP network's ban duration has expired, we can remove
|
||||
// its entry from the store.
|
||||
if !time.Now().Before(status.Expiration) {
|
||||
return removeBannedIPNet(banIndex, reasonIndex, k)
|
||||
}
|
||||
|
||||
banStatus = status
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return Status{}, err
|
||||
}
|
||||
|
||||
return banStatus, nil
|
||||
}
|
||||
|
||||
// fetchStatus retrieves the ban status of the given IP network.
|
||||
func fetchStatus(banIndex, reasonIndex walletdb.ReadWriteBucket,
|
||||
ipNetKey []byte) Status {
|
||||
|
||||
v := banIndex.Get(ipNetKey)
|
||||
if v == nil {
|
||||
return Status{}
|
||||
}
|
||||
reason := Reason(reasonIndex.Get(ipNetKey)[0])
|
||||
banExpiration := time.Unix(int64(byteOrder.Uint64(v)), 0)
|
||||
|
||||
return Status{
|
||||
Banned: true,
|
||||
Reason: reason,
|
||||
Expiration: banExpiration,
|
||||
}
|
||||
}
|
||||
|
||||
// removeBannedIPNet removes all references to a banned IP network within the
|
||||
// ban store.
|
||||
func removeBannedIPNet(banIndex, reasonIndex walletdb.ReadWriteBucket,
|
||||
ipNetKey []byte) error {
|
||||
|
||||
if err := banIndex.Delete(ipNetKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return reasonIndex.Delete(ipNetKey)
|
||||
}
|
||||
48
vendor/github.com/lightninglabs/neutrino/banman/util.go
generated
vendored
Normal file
48
vendor/github.com/lightninglabs/neutrino/banman/util.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package banman
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultIPv4Mask is the default IPv4 mask used when parsing IP
|
||||
// networks from an address. This ensures that the IP network only
|
||||
// contains *one* IP address -- the one specified.
|
||||
defaultIPv4Mask = net.CIDRMask(32, 32)
|
||||
|
||||
// defaultIPv6Mask is the default IPv6 mask used when parsing IP
|
||||
// networks from an address. This ensures that the IP network only
|
||||
// contains *one* IP address -- the one specified.
|
||||
defaultIPv6Mask = net.CIDRMask(128, 128)
|
||||
)
|
||||
|
||||
// ParseIPNet parses the IP network that contains the given address. An optional
|
||||
// mask can be provided, to expand the scope of the IP network, otherwise the
|
||||
// IP's default is used.
|
||||
//
|
||||
// NOTE: This assumes that the address has already been resolved.
|
||||
func ParseIPNet(addr string, mask net.IPMask) (*net.IPNet, error) {
|
||||
// If the address includes a port, we'll remove it.
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
// Address doesn't include a port.
|
||||
host = addr
|
||||
}
|
||||
|
||||
// Parse the IP from the host to ensure it is supported.
|
||||
ip := net.ParseIP(host)
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
if mask == nil {
|
||||
mask = defaultIPv4Mask
|
||||
}
|
||||
case ip.To16() != nil:
|
||||
if mask == nil {
|
||||
mask = defaultIPv6Mask
|
||||
}
|
||||
default:
|
||||
return nil, ErrUnsupportedIP
|
||||
}
|
||||
|
||||
return &net.IPNet{IP: ip.Mask(mask), Mask: mask}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user