Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
76
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/client.go
generated
vendored
Normal file
76
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/client.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/transport"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
var log = logging.Logger("p2p-circuit")
|
||||
|
||||
// Client implements the client-side of the p2p-circuit/v2 protocol:
|
||||
// - it implements dialing through v2 relays
|
||||
// - it listens for incoming connections through v2 relays.
|
||||
//
|
||||
// For backwards compatibility with v1 relays and older nodes, the client will
|
||||
// also accept relay connections through v1 relays and fallback dial peers using p2p-circuit/v1.
|
||||
// This allows us to use the v2 code as drop in replacement for v1 in a host without breaking
|
||||
// existing code and interoperability with older nodes.
|
||||
type Client struct {
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
host host.Host
|
||||
upgrader transport.Upgrader
|
||||
|
||||
incoming chan accept
|
||||
|
||||
mx sync.Mutex
|
||||
activeDials map[peer.ID]*completion
|
||||
hopCount map[peer.ID]int
|
||||
}
|
||||
|
||||
var _ io.Closer = &Client{}
|
||||
var _ transport.Transport = &Client{}
|
||||
|
||||
type accept struct {
|
||||
conn *Conn
|
||||
writeResponse func() error
|
||||
}
|
||||
|
||||
type completion struct {
|
||||
ch chan struct{}
|
||||
relay peer.ID
|
||||
err error
|
||||
}
|
||||
|
||||
// New constructs a new p2p-circuit/v2 client, attached to the given host and using the given
|
||||
// upgrader to perform connection upgrades.
|
||||
func New(h host.Host, upgrader transport.Upgrader) (*Client, error) {
|
||||
cl := &Client{
|
||||
host: h,
|
||||
upgrader: upgrader,
|
||||
incoming: make(chan accept),
|
||||
activeDials: make(map[peer.ID]*completion),
|
||||
hopCount: make(map[peer.ID]int),
|
||||
}
|
||||
cl.ctx, cl.ctxCancel = context.WithCancel(context.Background())
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
// Start registers the circuit (client) protocol stream handlers
|
||||
func (c *Client) Start() {
|
||||
c.host.SetStreamHandler(proto.ProtoIDv2Stop, c.handleStreamV2)
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.ctxCancel()
|
||||
c.host.RemoveStreamHandler(proto.ProtoIDv2Stop)
|
||||
return nil
|
||||
}
|
||||
163
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/conn.go
generated
vendored
Normal file
163
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/conn.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
tpt "github.com/libp2p/go-libp2p/core/transport"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
// HopTagWeight is the connection manager weight for connections carrying relay hop streams
|
||||
var HopTagWeight = 5
|
||||
|
||||
type statLimitDuration struct{}
|
||||
type statLimitData struct{}
|
||||
|
||||
var (
|
||||
StatLimitDuration = statLimitDuration{}
|
||||
StatLimitData = statLimitData{}
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
stream network.Stream
|
||||
remote peer.AddrInfo
|
||||
stat network.ConnStats
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
type NetAddr struct {
|
||||
Relay string
|
||||
Remote string
|
||||
}
|
||||
|
||||
var _ net.Addr = (*NetAddr)(nil)
|
||||
|
||||
func (n *NetAddr) Network() string {
|
||||
return "libp2p-circuit-relay"
|
||||
}
|
||||
|
||||
func (n *NetAddr) String() string {
|
||||
return fmt.Sprintf("relay[%s-%s]", n.Remote, n.Relay)
|
||||
}
|
||||
|
||||
// Conn interface
|
||||
var _ manet.Conn = (*Conn)(nil)
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
c.untagHop()
|
||||
return c.stream.Reset()
|
||||
}
|
||||
|
||||
func (c *Conn) Read(buf []byte) (int, error) {
|
||||
return c.stream.Read(buf)
|
||||
}
|
||||
|
||||
func (c *Conn) Write(buf []byte) (int, error) {
|
||||
return c.stream.Write(buf)
|
||||
}
|
||||
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return c.stream.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.stream.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return c.stream.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// TODO: is it okay to cast c.Conn().RemotePeer() into a multiaddr? might be "user input"
|
||||
func (c *Conn) RemoteMultiaddr() ma.Multiaddr {
|
||||
// TODO: We should be able to do this directly without converting to/from a string.
|
||||
relayAddr, err := ma.NewComponent(
|
||||
ma.ProtocolWithCode(ma.P_P2P).Name,
|
||||
c.stream.Conn().RemotePeer().String(),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ma.Join(c.stream.Conn().RemoteMultiaddr(), relayAddr, circuitAddr)
|
||||
}
|
||||
|
||||
func (c *Conn) LocalMultiaddr() ma.Multiaddr {
|
||||
return c.stream.Conn().LocalMultiaddr()
|
||||
}
|
||||
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
na, err := manet.ToNetAddr(c.stream.Conn().LocalMultiaddr())
|
||||
if err != nil {
|
||||
log.Error("failed to convert local multiaddr to net addr:", err)
|
||||
return nil
|
||||
}
|
||||
return na
|
||||
}
|
||||
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return &NetAddr{
|
||||
Relay: c.stream.Conn().RemotePeer().String(),
|
||||
Remote: c.remote.ID.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// ConnStat interface
|
||||
var _ network.ConnStat = (*Conn)(nil)
|
||||
|
||||
func (c *Conn) Stat() network.ConnStats {
|
||||
return c.stat
|
||||
}
|
||||
|
||||
// tagHop tags the underlying relay connection so that it can be (somewhat) protected from the
|
||||
// connection manager as it is an important connection that proxies other connections.
|
||||
// This is handled here so that the user code doesnt need to bother with this and avoid
|
||||
// clown shoes situations where a high value peer connection is behind a relayed connection and it is
|
||||
// implicitly because the connection manager closed the underlying relay connection.
|
||||
func (c *Conn) tagHop() {
|
||||
c.client.mx.Lock()
|
||||
defer c.client.mx.Unlock()
|
||||
|
||||
p := c.stream.Conn().RemotePeer()
|
||||
c.client.hopCount[p]++
|
||||
if c.client.hopCount[p] == 1 {
|
||||
c.client.host.ConnManager().TagPeer(p, "relay-hop-stream", HopTagWeight)
|
||||
}
|
||||
}
|
||||
|
||||
// untagHop removes the relay-hop-stream tag if necessary; it is invoked when a relayed connection
|
||||
// is closed.
|
||||
func (c *Conn) untagHop() {
|
||||
c.client.mx.Lock()
|
||||
defer c.client.mx.Unlock()
|
||||
|
||||
p := c.stream.Conn().RemotePeer()
|
||||
c.client.hopCount[p]--
|
||||
if c.client.hopCount[p] == 0 {
|
||||
c.client.host.ConnManager().UntagPeer(p, "relay-hop-stream")
|
||||
delete(c.client.hopCount, p)
|
||||
}
|
||||
}
|
||||
|
||||
type capableConnWithStat interface {
|
||||
tpt.CapableConn
|
||||
network.ConnStat
|
||||
}
|
||||
|
||||
type capableConn struct {
|
||||
capableConnWithStat
|
||||
}
|
||||
|
||||
var transportName = ma.ProtocolWithCode(ma.P_CIRCUIT).Name
|
||||
|
||||
func (c capableConn) ConnState() network.ConnectionState {
|
||||
return network.ConnectionState{
|
||||
Transport: transportName,
|
||||
}
|
||||
}
|
||||
189
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/dial.go
generated
vendored
Normal file
189
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/dial.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
const maxMessageSize = 4096
|
||||
|
||||
var DialTimeout = time.Minute
|
||||
var DialRelayTimeout = 5 * time.Second
|
||||
|
||||
// relay protocol errors; used for signalling deduplication
|
||||
type relayError struct {
|
||||
err string
|
||||
}
|
||||
|
||||
func (e relayError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func newRelayError(t string, args ...interface{}) error {
|
||||
return relayError{err: fmt.Sprintf(t, args...)}
|
||||
}
|
||||
|
||||
func isRelayError(err error) bool {
|
||||
_, ok := err.(relayError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// dialer
|
||||
func (c *Client) dial(ctx context.Context, a ma.Multiaddr, p peer.ID) (*Conn, error) {
|
||||
// split /a/p2p-circuit/b into (/a, /p2p-circuit/b)
|
||||
relayaddr, destaddr := ma.SplitFunc(a, func(c ma.Component) bool {
|
||||
return c.Protocol().Code == ma.P_CIRCUIT
|
||||
})
|
||||
|
||||
// If the address contained no /p2p-circuit part, the second part is nil.
|
||||
if destaddr == nil {
|
||||
return nil, fmt.Errorf("%s is not a relay address", a)
|
||||
}
|
||||
|
||||
if relayaddr == nil {
|
||||
return nil, fmt.Errorf("can't dial a p2p-circuit without specifying a relay: %s", a)
|
||||
}
|
||||
|
||||
dinfo := peer.AddrInfo{ID: p}
|
||||
|
||||
// Strip the /p2p-circuit prefix from the destaddr so that we can pass the destination address
|
||||
// (if present) for active relays
|
||||
_, destaddr = ma.SplitFirst(destaddr)
|
||||
if destaddr != nil {
|
||||
dinfo.Addrs = append(dinfo.Addrs, destaddr)
|
||||
}
|
||||
|
||||
rinfo, err := peer.AddrInfoFromP2pAddr(relayaddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing relay multiaddr '%s': %w", relayaddr, err)
|
||||
}
|
||||
|
||||
// deduplicate active relay dials to the same peer
|
||||
retry:
|
||||
c.mx.Lock()
|
||||
dedup, active := c.activeDials[p]
|
||||
if !active {
|
||||
dedup = &completion{ch: make(chan struct{}), relay: rinfo.ID}
|
||||
c.activeDials[p] = dedup
|
||||
}
|
||||
c.mx.Unlock()
|
||||
|
||||
if active {
|
||||
select {
|
||||
case <-dedup.ch:
|
||||
if dedup.err != nil {
|
||||
if dedup.relay != rinfo.ID {
|
||||
// different relay, retry
|
||||
goto retry
|
||||
}
|
||||
|
||||
if !isRelayError(dedup.err) {
|
||||
// not a relay protocol error, retry
|
||||
goto retry
|
||||
}
|
||||
|
||||
// don't try the same relay if it failed to connect with a protocol error
|
||||
return nil, fmt.Errorf("concurrent active dial through the same relay failed with a protocol error")
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("concurrent active dial succeeded")
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := c.dialPeer(ctx, *rinfo, dinfo)
|
||||
|
||||
c.mx.Lock()
|
||||
dedup.err = err
|
||||
close(dedup.ch)
|
||||
delete(c.activeDials, p)
|
||||
c.mx.Unlock()
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
func (c *Client) dialPeer(ctx context.Context, relay, dest peer.AddrInfo) (*Conn, error) {
|
||||
log.Debugf("dialing peer %s through relay %s", dest.ID, relay.ID)
|
||||
|
||||
if len(relay.Addrs) > 0 {
|
||||
c.host.Peerstore().AddAddrs(relay.ID, relay.Addrs, peerstore.TempAddrTTL)
|
||||
}
|
||||
|
||||
dialCtx, cancel := context.WithTimeout(ctx, DialRelayTimeout)
|
||||
defer cancel()
|
||||
s, err := c.host.NewStream(dialCtx, relay.ID, proto.ProtoIDv2Hop)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening hop stream to relay: %w", err)
|
||||
}
|
||||
return c.connect(s, dest)
|
||||
}
|
||||
|
||||
func (c *Client) connect(s network.Stream, dest peer.AddrInfo) (*Conn, error) {
|
||||
if err := s.Scope().ReserveMemory(maxMessageSize, network.ReservationPriorityAlways); err != nil {
|
||||
s.Reset()
|
||||
return nil, err
|
||||
}
|
||||
defer s.Scope().ReleaseMemory(maxMessageSize)
|
||||
|
||||
rd := util.NewDelimitedReader(s, maxMessageSize)
|
||||
wr := util.NewDelimitedWriter(s)
|
||||
defer rd.Close()
|
||||
|
||||
var msg pbv2.HopMessage
|
||||
|
||||
msg.Type = pbv2.HopMessage_CONNECT.Enum()
|
||||
msg.Peer = util.PeerInfoToPeerV2(dest)
|
||||
|
||||
s.SetDeadline(time.Now().Add(DialTimeout))
|
||||
|
||||
err := wr.WriteMsg(&msg)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg.Reset()
|
||||
|
||||
err = rd.ReadMsg(&msg)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.SetDeadline(time.Time{})
|
||||
|
||||
if msg.GetType() != pbv2.HopMessage_STATUS {
|
||||
s.Reset()
|
||||
return nil, newRelayError("unexpected relay response; not a status message (%d)", msg.GetType())
|
||||
}
|
||||
|
||||
status := msg.GetStatus()
|
||||
if status != pbv2.Status_OK {
|
||||
s.Reset()
|
||||
return nil, newRelayError("error opening relay circuit: %s (%d)", pbv2.Status_name[int32(status)], status)
|
||||
}
|
||||
|
||||
// check for a limit provided by the relay; if the limit is not nil, then this is a limited
|
||||
// relay connection and we mark the connection as transient.
|
||||
var stat network.ConnStats
|
||||
if limit := msg.GetLimit(); limit != nil {
|
||||
stat.Transient = true
|
||||
stat.Extra = make(map[interface{}]interface{})
|
||||
stat.Extra[StatLimitDuration] = time.Duration(limit.GetDuration()) * time.Second
|
||||
stat.Extra[StatLimitData] = limit.GetData()
|
||||
}
|
||||
|
||||
return &Conn{stream: s, remote: dest, stat: stat, client: c}, nil
|
||||
}
|
||||
88
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/handlers.go
generated
vendored
Normal file
88
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/handlers.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util"
|
||||
)
|
||||
|
||||
var (
|
||||
StreamTimeout = 1 * time.Minute
|
||||
AcceptTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
func (c *Client) handleStreamV2(s network.Stream) {
|
||||
log.Debugf("new relay/v2 stream from: %s", s.Conn().RemotePeer())
|
||||
|
||||
s.SetReadDeadline(time.Now().Add(StreamTimeout))
|
||||
|
||||
rd := util.NewDelimitedReader(s, maxMessageSize)
|
||||
defer rd.Close()
|
||||
|
||||
writeResponse := func(status pbv2.Status) error {
|
||||
wr := util.NewDelimitedWriter(s)
|
||||
|
||||
var msg pbv2.StopMessage
|
||||
msg.Type = pbv2.StopMessage_STATUS.Enum()
|
||||
msg.Status = status.Enum()
|
||||
|
||||
return wr.WriteMsg(&msg)
|
||||
}
|
||||
|
||||
handleError := func(status pbv2.Status) {
|
||||
log.Debugf("protocol error: %s (%d)", pbv2.Status_name[int32(status)], status)
|
||||
err := writeResponse(status)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
log.Debugf("error writing circuit response: %s", err.Error())
|
||||
} else {
|
||||
s.Close()
|
||||
}
|
||||
}
|
||||
|
||||
var msg pbv2.StopMessage
|
||||
|
||||
err := rd.ReadMsg(&msg)
|
||||
if err != nil {
|
||||
handleError(pbv2.Status_MALFORMED_MESSAGE)
|
||||
return
|
||||
}
|
||||
// reset stream deadline as message has been read
|
||||
s.SetReadDeadline(time.Time{})
|
||||
|
||||
if msg.GetType() != pbv2.StopMessage_CONNECT {
|
||||
handleError(pbv2.Status_UNEXPECTED_MESSAGE)
|
||||
return
|
||||
}
|
||||
|
||||
src, err := util.PeerToPeerInfoV2(msg.GetPeer())
|
||||
if err != nil {
|
||||
handleError(pbv2.Status_MALFORMED_MESSAGE)
|
||||
return
|
||||
}
|
||||
|
||||
// check for a limit provided by the relay; if the limit is not nil, then this is a limited
|
||||
// relay connection and we mark the connection as transient.
|
||||
var stat network.ConnStats
|
||||
if limit := msg.GetLimit(); limit != nil {
|
||||
stat.Transient = true
|
||||
stat.Extra = make(map[interface{}]interface{})
|
||||
stat.Extra[StatLimitDuration] = time.Duration(limit.GetDuration()) * time.Second
|
||||
stat.Extra[StatLimitData] = limit.GetData()
|
||||
}
|
||||
|
||||
log.Debugf("incoming relay connection from: %s", src.ID)
|
||||
|
||||
select {
|
||||
case c.incoming <- accept{
|
||||
conn: &Conn{stream: s, remote: src, stat: stat, client: c},
|
||||
writeResponse: func() error {
|
||||
return writeResponse(pbv2.Status_OK)
|
||||
},
|
||||
}:
|
||||
case <-time.After(AcceptTimeout):
|
||||
handleError(pbv2.Status_CONNECTION_FAILED)
|
||||
}
|
||||
}
|
||||
54
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/listen.go
generated
vendored
Normal file
54
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/listen.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/transport"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
var _ manet.Listener = (*Listener)(nil)
|
||||
|
||||
type Listener Client
|
||||
|
||||
func (c *Client) Listener() *Listener {
|
||||
return (*Listener)(c)
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (manet.Conn, error) {
|
||||
for {
|
||||
select {
|
||||
case evt := <-l.incoming:
|
||||
err := evt.writeResponse()
|
||||
if err != nil {
|
||||
log.Debugf("error writing relay response: %s", err.Error())
|
||||
evt.conn.stream.Reset()
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("accepted relay connection from %s through %s", evt.conn.remote.ID, evt.conn.RemoteMultiaddr())
|
||||
|
||||
evt.conn.tagHop()
|
||||
return evt.conn, nil
|
||||
|
||||
case <-l.ctx.Done():
|
||||
return nil, transport.ErrListenerClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return &NetAddr{
|
||||
Relay: "any",
|
||||
Remote: "any",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Multiaddr() ma.Multiaddr {
|
||||
return circuitAddr
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
return (*Client)(l).Close()
|
||||
}
|
||||
159
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/reservation.go
generated
vendored
Normal file
159
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/reservation.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
"github.com/libp2p/go-libp2p/core/record"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var ReserveTimeout = time.Minute
|
||||
|
||||
// Reservation is a struct carrying information about a relay/v2 slot reservation.
|
||||
type Reservation struct {
|
||||
// Expiration is the expiration time of the reservation
|
||||
Expiration time.Time
|
||||
// Addrs contains the vouched public addresses of the reserving peer, which can be
|
||||
// announced to the network
|
||||
Addrs []ma.Multiaddr
|
||||
|
||||
// LimitDuration is the time limit for which the relay will keep a relayed connection
|
||||
// open. If 0, there is no limit.
|
||||
LimitDuration time.Duration
|
||||
// LimitData is the number of bytes that the relay will relay in each direction before
|
||||
// resetting a relayed connection.
|
||||
LimitData uint64
|
||||
|
||||
// Voucher is a signed reservation voucher provided by the relay
|
||||
Voucher *proto.ReservationVoucher
|
||||
}
|
||||
|
||||
// ReservationError is the error returned on failure to reserve a slot in the relay
|
||||
type ReservationError struct {
|
||||
|
||||
// Status is the status returned by the relay for rejecting the reservation
|
||||
// request. It is set to pbv2.Status_CONNECTION_FAILED on other failures
|
||||
Status pbv2.Status
|
||||
|
||||
// Reason is the reason for reservation failure
|
||||
Reason string
|
||||
|
||||
err error
|
||||
}
|
||||
|
||||
func (re ReservationError) Error() string {
|
||||
return fmt.Sprintf("reservation error: status: %s reason: %s err: %s", pbv2.Status_name[int32(re.Status)], re.Reason, re.err)
|
||||
}
|
||||
|
||||
func (re ReservationError) Unwrap() error {
|
||||
return re.err
|
||||
}
|
||||
|
||||
// Reserve reserves a slot in a relay and returns the reservation information.
|
||||
// Clients must reserve slots in order for the relay to relay connections to them.
|
||||
func Reserve(ctx context.Context, h host.Host, ai peer.AddrInfo) (*Reservation, error) {
|
||||
if len(ai.Addrs) > 0 {
|
||||
h.Peerstore().AddAddrs(ai.ID, ai.Addrs, peerstore.TempAddrTTL)
|
||||
}
|
||||
|
||||
s, err := h.NewStream(ctx, ai.ID, proto.ProtoIDv2Hop)
|
||||
if err != nil {
|
||||
return nil, ReservationError{Status: pbv2.Status_CONNECTION_FAILED, Reason: "failed to open stream", err: err}
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
rd := util.NewDelimitedReader(s, maxMessageSize)
|
||||
wr := util.NewDelimitedWriter(s)
|
||||
defer rd.Close()
|
||||
|
||||
var msg pbv2.HopMessage
|
||||
msg.Type = pbv2.HopMessage_RESERVE.Enum()
|
||||
|
||||
s.SetDeadline(time.Now().Add(ReserveTimeout))
|
||||
|
||||
if err := wr.WriteMsg(&msg); err != nil {
|
||||
s.Reset()
|
||||
return nil, ReservationError{Status: pbv2.Status_CONNECTION_FAILED, Reason: "error writing reservation message", err: err}
|
||||
}
|
||||
|
||||
msg.Reset()
|
||||
|
||||
if err := rd.ReadMsg(&msg); err != nil {
|
||||
s.Reset()
|
||||
return nil, ReservationError{Status: pbv2.Status_CONNECTION_FAILED, Reason: "error reading reservation response message: %w", err: err}
|
||||
}
|
||||
|
||||
if msg.GetType() != pbv2.HopMessage_STATUS {
|
||||
return nil, ReservationError{
|
||||
Status: pbv2.Status_MALFORMED_MESSAGE,
|
||||
Reason: fmt.Sprintf("unexpected relay response: not a status message (%d)", msg.GetType()),
|
||||
err: err}
|
||||
}
|
||||
|
||||
if status := msg.GetStatus(); status != pbv2.Status_OK {
|
||||
return nil, ReservationError{Status: msg.GetStatus(), Reason: "reservation failed"}
|
||||
}
|
||||
|
||||
rsvp := msg.GetReservation()
|
||||
if rsvp == nil {
|
||||
return nil, ReservationError{Status: pbv2.Status_MALFORMED_MESSAGE, Reason: "missing reservation info"}
|
||||
}
|
||||
|
||||
result := &Reservation{}
|
||||
result.Expiration = time.Unix(int64(rsvp.GetExpire()), 0)
|
||||
if result.Expiration.Before(time.Now()) {
|
||||
return nil, ReservationError{
|
||||
Status: pbv2.Status_MALFORMED_MESSAGE,
|
||||
Reason: fmt.Sprintf("received reservation with expiration date in the past: %s", result.Expiration),
|
||||
}
|
||||
}
|
||||
|
||||
addrs := rsvp.GetAddrs()
|
||||
result.Addrs = make([]ma.Multiaddr, 0, len(addrs))
|
||||
for _, ab := range addrs {
|
||||
a, err := ma.NewMultiaddrBytes(ab)
|
||||
if err != nil {
|
||||
log.Warnf("ignoring unparsable relay address: %s", err)
|
||||
continue
|
||||
}
|
||||
result.Addrs = append(result.Addrs, a)
|
||||
}
|
||||
|
||||
voucherBytes := rsvp.GetVoucher()
|
||||
if voucherBytes != nil {
|
||||
_, rec, err := record.ConsumeEnvelope(voucherBytes, proto.RecordDomain)
|
||||
if err != nil {
|
||||
return nil, ReservationError{
|
||||
Status: pbv2.Status_MALFORMED_MESSAGE,
|
||||
Reason: fmt.Sprintf("error consuming voucher envelope: %s", err),
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
voucher, ok := rec.(*proto.ReservationVoucher)
|
||||
if !ok {
|
||||
return nil, ReservationError{
|
||||
Status: pbv2.Status_MALFORMED_MESSAGE,
|
||||
Reason: fmt.Sprintf("unexpected voucher record type: %+T", rec),
|
||||
}
|
||||
}
|
||||
result.Voucher = voucher
|
||||
}
|
||||
|
||||
limit := msg.GetLimit()
|
||||
if limit != nil {
|
||||
result.LimitDuration = time.Duration(limit.GetDuration()) * time.Second
|
||||
result.LimitData = limit.GetData()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
100
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/transport.go
generated
vendored
Normal file
100
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client/transport.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/transport"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var circuitProtocol = ma.ProtocolWithCode(ma.P_CIRCUIT)
|
||||
var circuitAddr = ma.Cast(circuitProtocol.VCode)
|
||||
|
||||
// AddTransport constructs a new p2p-circuit/v2 client and adds it as a transport to the
|
||||
// host network
|
||||
func AddTransport(h host.Host, upgrader transport.Upgrader) error {
|
||||
n, ok := h.Network().(transport.TransportNetwork)
|
||||
if !ok {
|
||||
return fmt.Errorf("%v is not a transport network", h.Network())
|
||||
}
|
||||
|
||||
c, err := New(h, upgrader)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error constructing circuit client: %w", err)
|
||||
}
|
||||
|
||||
err = n.AddTransport(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding circuit transport: %w", err)
|
||||
}
|
||||
|
||||
err = n.Listen(circuitAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listening to circuit addr: %w", err)
|
||||
}
|
||||
|
||||
c.Start()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transport interface
|
||||
var _ transport.Transport = (*Client)(nil)
|
||||
var _ io.Closer = (*Client)(nil)
|
||||
|
||||
func (c *Client) Dial(ctx context.Context, a ma.Multiaddr, p peer.ID) (transport.CapableConn, error) {
|
||||
connScope, err := c.host.Network().ResourceManager().OpenConnection(network.DirOutbound, false, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := c.dialAndUpgrade(ctx, a, p, connScope)
|
||||
if err != nil {
|
||||
connScope.Done()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialAndUpgrade(ctx context.Context, a ma.Multiaddr, p peer.ID, connScope network.ConnManagementScope) (transport.CapableConn, error) {
|
||||
if err := connScope.SetPeer(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := c.dial(ctx, a, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.tagHop()
|
||||
cc, err := c.upgrader.Upgrade(ctx, c, conn, network.DirOutbound, p, connScope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return capableConn{cc.(capableConnWithStat)}, nil
|
||||
}
|
||||
|
||||
func (c *Client) CanDial(addr ma.Multiaddr) bool {
|
||||
_, err := addr.ValueForProtocol(ma.P_CIRCUIT)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (c *Client) Listen(addr ma.Multiaddr) (transport.Listener, error) {
|
||||
// TODO connect to the relay and reserve slot if specified
|
||||
if _, err := addr.ValueForProtocol(ma.P_CIRCUIT); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.upgrader.UpgradeListener(c, c.Listener()), nil
|
||||
}
|
||||
|
||||
func (c *Client) Protocols() []int {
|
||||
return []int{ma.P_CIRCUIT}
|
||||
}
|
||||
|
||||
func (c *Client) Proxy() bool {
|
||||
return true
|
||||
}
|
||||
727
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/circuit.pb.go
generated
vendored
Normal file
727
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/circuit.pb.go
generated
vendored
Normal file
@@ -0,0 +1,727 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.21.12
|
||||
// source: pb/circuit.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Status int32
|
||||
|
||||
const (
|
||||
// zero value field required for proto3 compatibility
|
||||
Status_UNUSED Status = 0
|
||||
Status_OK Status = 100
|
||||
Status_RESERVATION_REFUSED Status = 200
|
||||
Status_RESOURCE_LIMIT_EXCEEDED Status = 201
|
||||
Status_PERMISSION_DENIED Status = 202
|
||||
Status_CONNECTION_FAILED Status = 203
|
||||
Status_NO_RESERVATION Status = 204
|
||||
Status_MALFORMED_MESSAGE Status = 400
|
||||
Status_UNEXPECTED_MESSAGE Status = 401
|
||||
)
|
||||
|
||||
// Enum value maps for Status.
|
||||
var (
|
||||
Status_name = map[int32]string{
|
||||
0: "UNUSED",
|
||||
100: "OK",
|
||||
200: "RESERVATION_REFUSED",
|
||||
201: "RESOURCE_LIMIT_EXCEEDED",
|
||||
202: "PERMISSION_DENIED",
|
||||
203: "CONNECTION_FAILED",
|
||||
204: "NO_RESERVATION",
|
||||
400: "MALFORMED_MESSAGE",
|
||||
401: "UNEXPECTED_MESSAGE",
|
||||
}
|
||||
Status_value = map[string]int32{
|
||||
"UNUSED": 0,
|
||||
"OK": 100,
|
||||
"RESERVATION_REFUSED": 200,
|
||||
"RESOURCE_LIMIT_EXCEEDED": 201,
|
||||
"PERMISSION_DENIED": 202,
|
||||
"CONNECTION_FAILED": 203,
|
||||
"NO_RESERVATION": 204,
|
||||
"MALFORMED_MESSAGE": 400,
|
||||
"UNEXPECTED_MESSAGE": 401,
|
||||
}
|
||||
)
|
||||
|
||||
func (x Status) Enum() *Status {
|
||||
p := new(Status)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Status) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (Status) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_pb_circuit_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (Status) Type() protoreflect.EnumType {
|
||||
return &file_pb_circuit_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x Status) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Status.Descriptor instead.
|
||||
func (Status) EnumDescriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type HopMessage_Type int32
|
||||
|
||||
const (
|
||||
HopMessage_RESERVE HopMessage_Type = 0
|
||||
HopMessage_CONNECT HopMessage_Type = 1
|
||||
HopMessage_STATUS HopMessage_Type = 2
|
||||
)
|
||||
|
||||
// Enum value maps for HopMessage_Type.
|
||||
var (
|
||||
HopMessage_Type_name = map[int32]string{
|
||||
0: "RESERVE",
|
||||
1: "CONNECT",
|
||||
2: "STATUS",
|
||||
}
|
||||
HopMessage_Type_value = map[string]int32{
|
||||
"RESERVE": 0,
|
||||
"CONNECT": 1,
|
||||
"STATUS": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x HopMessage_Type) Enum() *HopMessage_Type {
|
||||
p := new(HopMessage_Type)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x HopMessage_Type) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (HopMessage_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_pb_circuit_proto_enumTypes[1].Descriptor()
|
||||
}
|
||||
|
||||
func (HopMessage_Type) Type() protoreflect.EnumType {
|
||||
return &file_pb_circuit_proto_enumTypes[1]
|
||||
}
|
||||
|
||||
func (x HopMessage_Type) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HopMessage_Type.Descriptor instead.
|
||||
func (HopMessage_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
type StopMessage_Type int32
|
||||
|
||||
const (
|
||||
StopMessage_CONNECT StopMessage_Type = 0
|
||||
StopMessage_STATUS StopMessage_Type = 1
|
||||
)
|
||||
|
||||
// Enum value maps for StopMessage_Type.
|
||||
var (
|
||||
StopMessage_Type_name = map[int32]string{
|
||||
0: "CONNECT",
|
||||
1: "STATUS",
|
||||
}
|
||||
StopMessage_Type_value = map[string]int32{
|
||||
"CONNECT": 0,
|
||||
"STATUS": 1,
|
||||
}
|
||||
)
|
||||
|
||||
func (x StopMessage_Type) Enum() *StopMessage_Type {
|
||||
p := new(StopMessage_Type)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x StopMessage_Type) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (StopMessage_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_pb_circuit_proto_enumTypes[2].Descriptor()
|
||||
}
|
||||
|
||||
func (StopMessage_Type) Type() protoreflect.EnumType {
|
||||
return &file_pb_circuit_proto_enumTypes[2]
|
||||
}
|
||||
|
||||
func (x StopMessage_Type) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StopMessage_Type.Descriptor instead.
|
||||
func (StopMessage_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{1, 0}
|
||||
}
|
||||
|
||||
type HopMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
Type *HopMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=circuit.pb.HopMessage_Type,oneof" json:"type,omitempty"`
|
||||
Peer *Peer `protobuf:"bytes,2,opt,name=peer,proto3,oneof" json:"peer,omitempty"`
|
||||
Reservation *Reservation `protobuf:"bytes,3,opt,name=reservation,proto3,oneof" json:"reservation,omitempty"`
|
||||
Limit *Limit `protobuf:"bytes,4,opt,name=limit,proto3,oneof" json:"limit,omitempty"`
|
||||
Status *Status `protobuf:"varint,5,opt,name=status,proto3,enum=circuit.pb.Status,oneof" json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HopMessage) Reset() {
|
||||
*x = HopMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_circuit_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HopMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HopMessage) ProtoMessage() {}
|
||||
|
||||
func (x *HopMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_circuit_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HopMessage.ProtoReflect.Descriptor instead.
|
||||
func (*HopMessage) Descriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HopMessage) GetType() HopMessage_Type {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return HopMessage_RESERVE
|
||||
}
|
||||
|
||||
func (x *HopMessage) GetPeer() *Peer {
|
||||
if x != nil {
|
||||
return x.Peer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HopMessage) GetReservation() *Reservation {
|
||||
if x != nil {
|
||||
return x.Reservation
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HopMessage) GetLimit() *Limit {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HopMessage) GetStatus() Status {
|
||||
if x != nil && x.Status != nil {
|
||||
return *x.Status
|
||||
}
|
||||
return Status_UNUSED
|
||||
}
|
||||
|
||||
type StopMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
Type *StopMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=circuit.pb.StopMessage_Type,oneof" json:"type,omitempty"`
|
||||
Peer *Peer `protobuf:"bytes,2,opt,name=peer,proto3,oneof" json:"peer,omitempty"`
|
||||
Limit *Limit `protobuf:"bytes,3,opt,name=limit,proto3,oneof" json:"limit,omitempty"`
|
||||
Status *Status `protobuf:"varint,4,opt,name=status,proto3,enum=circuit.pb.Status,oneof" json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (x *StopMessage) Reset() {
|
||||
*x = StopMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_circuit_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StopMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StopMessage) ProtoMessage() {}
|
||||
|
||||
func (x *StopMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_circuit_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StopMessage.ProtoReflect.Descriptor instead.
|
||||
func (*StopMessage) Descriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *StopMessage) GetType() StopMessage_Type {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return StopMessage_CONNECT
|
||||
}
|
||||
|
||||
func (x *StopMessage) GetPeer() *Peer {
|
||||
if x != nil {
|
||||
return x.Peer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StopMessage) GetLimit() *Limit {
|
||||
if x != nil {
|
||||
return x.Limit
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StopMessage) GetStatus() Status {
|
||||
if x != nil && x.Status != nil {
|
||||
return *x.Status
|
||||
}
|
||||
return Status_UNUSED
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
Id []byte `protobuf:"bytes,1,opt,name=id,proto3,oneof" json:"id,omitempty"`
|
||||
Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs,proto3" json:"addrs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Peer) Reset() {
|
||||
*x = Peer{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_circuit_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Peer) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Peer) ProtoMessage() {}
|
||||
|
||||
func (x *Peer) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_circuit_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Peer.ProtoReflect.Descriptor instead.
|
||||
func (*Peer) Descriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Peer) GetId() []byte {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Peer) GetAddrs() [][]byte {
|
||||
if x != nil {
|
||||
return x.Addrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Reservation struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
Expire *uint64 `protobuf:"varint,1,opt,name=expire,proto3,oneof" json:"expire,omitempty"` // Unix expiration time (UTC)
|
||||
Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs,proto3" json:"addrs,omitempty"` // relay addrs for reserving peer
|
||||
Voucher []byte `protobuf:"bytes,3,opt,name=voucher,proto3,oneof" json:"voucher,omitempty"` // reservation voucher
|
||||
}
|
||||
|
||||
func (x *Reservation) Reset() {
|
||||
*x = Reservation{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_circuit_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Reservation) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Reservation) ProtoMessage() {}
|
||||
|
||||
func (x *Reservation) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_circuit_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Reservation.ProtoReflect.Descriptor instead.
|
||||
func (*Reservation) Descriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *Reservation) GetExpire() uint64 {
|
||||
if x != nil && x.Expire != nil {
|
||||
return *x.Expire
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Reservation) GetAddrs() [][]byte {
|
||||
if x != nil {
|
||||
return x.Addrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Reservation) GetVoucher() []byte {
|
||||
if x != nil {
|
||||
return x.Voucher
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Limit struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Duration *uint32 `protobuf:"varint,1,opt,name=duration,proto3,oneof" json:"duration,omitempty"` // seconds
|
||||
Data *uint64 `protobuf:"varint,2,opt,name=data,proto3,oneof" json:"data,omitempty"` // bytes
|
||||
}
|
||||
|
||||
func (x *Limit) Reset() {
|
||||
*x = Limit{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_circuit_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Limit) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Limit) ProtoMessage() {}
|
||||
|
||||
func (x *Limit) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_circuit_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Limit.ProtoReflect.Descriptor instead.
|
||||
func (*Limit) Descriptor() ([]byte, []int) {
|
||||
return file_pb_circuit_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *Limit) GetDuration() uint32 {
|
||||
if x != nil && x.Duration != nil {
|
||||
return *x.Duration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Limit) GetData() uint64 {
|
||||
if x != nil && x.Data != nil {
|
||||
return *x.Data
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_pb_circuit_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pb_circuit_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x70, 0x62, 0x2f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x22, 0xf1,
|
||||
0x02, 0x0a, 0x0a, 0x48, 0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a,
|
||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x63, 0x69,
|
||||
0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
|
||||
0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x10, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50,
|
||||
0x65, 0x65, 0x72, 0x48, 0x01, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x3e,
|
||||
0x0a, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x02, 0x52, 0x0b,
|
||||
0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2c,
|
||||
0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e,
|
||||
0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74,
|
||||
0x48, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x06,
|
||||
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63,
|
||||
0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||
0x48, 0x04, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x22, 0x2c, 0x0a,
|
||||
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45,
|
||||
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x01, 0x12,
|
||||
0x0a, 0x0a, 0x06, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x10, 0x02, 0x42, 0x07, 0x0a, 0x05, 0x5f,
|
||||
0x74, 0x79, 0x70, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x42, 0x0e, 0x0a,
|
||||
0x0c, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0x0a,
|
||||
0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74,
|
||||
0x75, 0x73, 0x22, 0x96, 0x02, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||
0x32, 0x1c, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74,
|
||||
0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x48, 0x00,
|
||||
0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x65, 0x65,
|
||||
0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69,
|
||||
0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x48, 0x01, 0x52, 0x04, 0x70, 0x65, 0x65,
|
||||
0x72, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x48, 0x02, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88,
|
||||
0x01, 0x01, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x03, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||
0x88, 0x01, 0x01, 0x22, 0x1f, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43,
|
||||
0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x41, 0x54,
|
||||
0x55, 0x53, 0x10, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x07, 0x0a,
|
||||
0x05, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74,
|
||||
0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x38, 0x0a, 0x04, 0x50,
|
||||
0x65, 0x65, 0x72, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72,
|
||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x42, 0x05,
|
||||
0x0a, 0x03, 0x5f, 0x69, 0x64, 0x22, 0x76, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x88, 0x01,
|
||||
0x01, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c,
|
||||
0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x6f, 0x75, 0x63, 0x68,
|
||||
0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x76, 0x6f, 0x75, 0x63,
|
||||
0x68, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72,
|
||||
0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x22, 0x57, 0x0a,
|
||||
0x05, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1f, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01,
|
||||
0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a,
|
||||
0x05, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x2a, 0xca, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75,
|
||||
0x73, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x4e, 0x55, 0x53, 0x45, 0x44, 0x10, 0x00, 0x12, 0x06, 0x0a,
|
||||
0x02, 0x4f, 0x4b, 0x10, 0x64, 0x12, 0x18, 0x0a, 0x13, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x41,
|
||||
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x53, 0x45, 0x44, 0x10, 0xc8, 0x01, 0x12,
|
||||
0x1c, 0x0a, 0x17, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4c, 0x49, 0x4d, 0x49,
|
||||
0x54, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0xc9, 0x01, 0x12, 0x16, 0x0a,
|
||||
0x11, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x49,
|
||||
0x45, 0x44, 0x10, 0xca, 0x01, 0x12, 0x16, 0x0a, 0x11, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54,
|
||||
0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0xcb, 0x01, 0x12, 0x13, 0x0a,
|
||||
0x0e, 0x4e, 0x4f, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10,
|
||||
0xcc, 0x01, 0x12, 0x16, 0x0a, 0x11, 0x4d, 0x41, 0x4c, 0x46, 0x4f, 0x52, 0x4d, 0x45, 0x44, 0x5f,
|
||||
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x90, 0x03, 0x12, 0x17, 0x0a, 0x12, 0x55, 0x4e,
|
||||
0x45, 0x58, 0x50, 0x45, 0x43, 0x54, 0x45, 0x44, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
|
||||
0x10, 0x91, 0x03, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_pb_circuit_proto_rawDescOnce sync.Once
|
||||
file_pb_circuit_proto_rawDescData = file_pb_circuit_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_pb_circuit_proto_rawDescGZIP() []byte {
|
||||
file_pb_circuit_proto_rawDescOnce.Do(func() {
|
||||
file_pb_circuit_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_circuit_proto_rawDescData)
|
||||
})
|
||||
return file_pb_circuit_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_pb_circuit_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||
var file_pb_circuit_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_pb_circuit_proto_goTypes = []interface{}{
|
||||
(Status)(0), // 0: circuit.pb.Status
|
||||
(HopMessage_Type)(0), // 1: circuit.pb.HopMessage.Type
|
||||
(StopMessage_Type)(0), // 2: circuit.pb.StopMessage.Type
|
||||
(*HopMessage)(nil), // 3: circuit.pb.HopMessage
|
||||
(*StopMessage)(nil), // 4: circuit.pb.StopMessage
|
||||
(*Peer)(nil), // 5: circuit.pb.Peer
|
||||
(*Reservation)(nil), // 6: circuit.pb.Reservation
|
||||
(*Limit)(nil), // 7: circuit.pb.Limit
|
||||
}
|
||||
var file_pb_circuit_proto_depIdxs = []int32{
|
||||
1, // 0: circuit.pb.HopMessage.type:type_name -> circuit.pb.HopMessage.Type
|
||||
5, // 1: circuit.pb.HopMessage.peer:type_name -> circuit.pb.Peer
|
||||
6, // 2: circuit.pb.HopMessage.reservation:type_name -> circuit.pb.Reservation
|
||||
7, // 3: circuit.pb.HopMessage.limit:type_name -> circuit.pb.Limit
|
||||
0, // 4: circuit.pb.HopMessage.status:type_name -> circuit.pb.Status
|
||||
2, // 5: circuit.pb.StopMessage.type:type_name -> circuit.pb.StopMessage.Type
|
||||
5, // 6: circuit.pb.StopMessage.peer:type_name -> circuit.pb.Peer
|
||||
7, // 7: circuit.pb.StopMessage.limit:type_name -> circuit.pb.Limit
|
||||
0, // 8: circuit.pb.StopMessage.status:type_name -> circuit.pb.Status
|
||||
9, // [9:9] is the sub-list for method output_type
|
||||
9, // [9:9] is the sub-list for method input_type
|
||||
9, // [9:9] is the sub-list for extension type_name
|
||||
9, // [9:9] is the sub-list for extension extendee
|
||||
0, // [0:9] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pb_circuit_proto_init() }
|
||||
func file_pb_circuit_proto_init() {
|
||||
if File_pb_circuit_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_pb_circuit_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HopMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pb_circuit_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*StopMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pb_circuit_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Peer); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pb_circuit_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Reservation); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pb_circuit_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Limit); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_pb_circuit_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
file_pb_circuit_proto_msgTypes[1].OneofWrappers = []interface{}{}
|
||||
file_pb_circuit_proto_msgTypes[2].OneofWrappers = []interface{}{}
|
||||
file_pb_circuit_proto_msgTypes[3].OneofWrappers = []interface{}{}
|
||||
file_pb_circuit_proto_msgTypes[4].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pb_circuit_proto_rawDesc,
|
||||
NumEnums: 3,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_pb_circuit_proto_goTypes,
|
||||
DependencyIndexes: file_pb_circuit_proto_depIdxs,
|
||||
EnumInfos: file_pb_circuit_proto_enumTypes,
|
||||
MessageInfos: file_pb_circuit_proto_msgTypes,
|
||||
}.Build()
|
||||
File_pb_circuit_proto = out.File
|
||||
file_pb_circuit_proto_rawDesc = nil
|
||||
file_pb_circuit_proto_goTypes = nil
|
||||
file_pb_circuit_proto_depIdxs = nil
|
||||
}
|
||||
70
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/circuit.proto
generated
vendored
Normal file
70
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/circuit.proto
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package circuit.pb;
|
||||
|
||||
message HopMessage {
|
||||
enum Type {
|
||||
RESERVE = 0;
|
||||
CONNECT = 1;
|
||||
STATUS = 2;
|
||||
}
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
optional Type type = 1;
|
||||
|
||||
optional Peer peer = 2;
|
||||
optional Reservation reservation = 3;
|
||||
optional Limit limit = 4;
|
||||
|
||||
optional Status status = 5;
|
||||
}
|
||||
|
||||
message StopMessage {
|
||||
enum Type {
|
||||
CONNECT = 0;
|
||||
STATUS = 1;
|
||||
}
|
||||
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
optional Type type = 1;
|
||||
|
||||
optional Peer peer = 2;
|
||||
optional Limit limit = 3;
|
||||
|
||||
optional Status status = 4;
|
||||
}
|
||||
|
||||
message Peer {
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
optional bytes id = 1;
|
||||
repeated bytes addrs = 2;
|
||||
}
|
||||
|
||||
message Reservation {
|
||||
// This field is marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set this.
|
||||
optional uint64 expire = 1; // Unix expiration time (UTC)
|
||||
repeated bytes addrs = 2; // relay addrs for reserving peer
|
||||
optional bytes voucher = 3; // reservation voucher
|
||||
}
|
||||
|
||||
message Limit {
|
||||
optional uint32 duration = 1; // seconds
|
||||
optional uint64 data = 2; // bytes
|
||||
}
|
||||
|
||||
enum Status {
|
||||
// zero value field required for proto3 compatibility
|
||||
UNUSED = 0;
|
||||
OK = 100;
|
||||
RESERVATION_REFUSED = 200;
|
||||
RESOURCE_LIMIT_EXCEEDED = 201;
|
||||
PERMISSION_DENIED = 202;
|
||||
CONNECTION_FAILED = 203;
|
||||
NO_RESERVATION = 204;
|
||||
MALFORMED_MESSAGE = 400;
|
||||
UNEXPECTED_MESSAGE = 401;
|
||||
}
|
||||
167
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/voucher.pb.go
generated
vendored
Normal file
167
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/voucher.pb.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.21.12
|
||||
// source: pb/voucher.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ReservationVoucher struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// These fields are marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set these.
|
||||
Relay []byte `protobuf:"bytes,1,opt,name=relay,proto3,oneof" json:"relay,omitempty"`
|
||||
Peer []byte `protobuf:"bytes,2,opt,name=peer,proto3,oneof" json:"peer,omitempty"`
|
||||
Expiration *uint64 `protobuf:"varint,3,opt,name=expiration,proto3,oneof" json:"expiration,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ReservationVoucher) Reset() {
|
||||
*x = ReservationVoucher{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_voucher_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ReservationVoucher) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ReservationVoucher) ProtoMessage() {}
|
||||
|
||||
func (x *ReservationVoucher) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_voucher_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ReservationVoucher.ProtoReflect.Descriptor instead.
|
||||
func (*ReservationVoucher) Descriptor() ([]byte, []int) {
|
||||
return file_pb_voucher_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ReservationVoucher) GetRelay() []byte {
|
||||
if x != nil {
|
||||
return x.Relay
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ReservationVoucher) GetPeer() []byte {
|
||||
if x != nil {
|
||||
return x.Peer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ReservationVoucher) GetExpiration() uint64 {
|
||||
if x != nil && x.Expiration != nil {
|
||||
return *x.Expiration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_pb_voucher_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pb_voucher_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x70, 0x62, 0x2f, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x0a, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x70, 0x62, 0x22, 0x8f,
|
||||
0x01, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x6f,
|
||||
0x75, 0x63, 0x68, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x88, 0x01, 0x01,
|
||||
0x12, 0x17, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01,
|
||||
0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x65, 0x78, 0x70,
|
||||
0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x02, 0x52,
|
||||
0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x08,
|
||||
0x0a, 0x06, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x65, 0x65,
|
||||
0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_pb_voucher_proto_rawDescOnce sync.Once
|
||||
file_pb_voucher_proto_rawDescData = file_pb_voucher_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_pb_voucher_proto_rawDescGZIP() []byte {
|
||||
file_pb_voucher_proto_rawDescOnce.Do(func() {
|
||||
file_pb_voucher_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_voucher_proto_rawDescData)
|
||||
})
|
||||
return file_pb_voucher_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_pb_voucher_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_pb_voucher_proto_goTypes = []interface{}{
|
||||
(*ReservationVoucher)(nil), // 0: circuit.pb.ReservationVoucher
|
||||
}
|
||||
var file_pb_voucher_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pb_voucher_proto_init() }
|
||||
func file_pb_voucher_proto_init() {
|
||||
if File_pb_voucher_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_pb_voucher_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ReservationVoucher); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_pb_voucher_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pb_voucher_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_pb_voucher_proto_goTypes,
|
||||
DependencyIndexes: file_pb_voucher_proto_depIdxs,
|
||||
MessageInfos: file_pb_voucher_proto_msgTypes,
|
||||
}.Build()
|
||||
File_pb_voucher_proto = out.File
|
||||
file_pb_voucher_proto_rawDesc = nil
|
||||
file_pb_voucher_proto_goTypes = nil
|
||||
file_pb_voucher_proto_depIdxs = nil
|
||||
}
|
||||
11
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/voucher.proto
generated
vendored
Normal file
11
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb/voucher.proto
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package circuit.pb;
|
||||
|
||||
message ReservationVoucher {
|
||||
// These fields are marked optional for backwards compatibility with proto2.
|
||||
// Users should make sure to always set these.
|
||||
optional bytes relay = 1;
|
||||
optional bytes peer = 2;
|
||||
optional uint64 expiration = 3;
|
||||
}
|
||||
6
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto/protocol.go
generated
vendored
Normal file
6
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto/protocol.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package proto
|
||||
|
||||
const (
|
||||
ProtoIDv2Hop = "/libp2p/circuit/relay/0.2.0/hop"
|
||||
ProtoIDv2Stop = "/libp2p/circuit/relay/0.2.0/stop"
|
||||
)
|
||||
69
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto/voucher.go
generated
vendored
Normal file
69
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto/voucher.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package proto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/record"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const RecordDomain = "libp2p-relay-rsvp"
|
||||
|
||||
// TODO: register in multicodec table in https://github.com/multiformats/multicodec
|
||||
var RecordCodec = []byte{0x03, 0x02}
|
||||
|
||||
func init() {
|
||||
record.RegisterType(&ReservationVoucher{})
|
||||
}
|
||||
|
||||
type ReservationVoucher struct {
|
||||
// Relay is the ID of the peer providing relay service
|
||||
Relay peer.ID
|
||||
// Peer is the ID of the peer receiving relay service through Relay
|
||||
Peer peer.ID
|
||||
// Expiration is the expiration time of the reservation
|
||||
Expiration time.Time
|
||||
}
|
||||
|
||||
var _ record.Record = (*ReservationVoucher)(nil)
|
||||
|
||||
func (rv *ReservationVoucher) Domain() string {
|
||||
return RecordDomain
|
||||
}
|
||||
|
||||
func (rv *ReservationVoucher) Codec() []byte {
|
||||
return RecordCodec
|
||||
}
|
||||
|
||||
func (rv *ReservationVoucher) MarshalRecord() ([]byte, error) {
|
||||
expiration := uint64(rv.Expiration.Unix())
|
||||
return proto.Marshal(&pbv2.ReservationVoucher{
|
||||
Relay: []byte(rv.Relay),
|
||||
Peer: []byte(rv.Peer),
|
||||
Expiration: &expiration,
|
||||
})
|
||||
}
|
||||
|
||||
func (rv *ReservationVoucher) UnmarshalRecord(blob []byte) error {
|
||||
pbrv := pbv2.ReservationVoucher{}
|
||||
err := proto.Unmarshal(blob, &pbrv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rv.Relay, err = peer.IDFromBytes(pbrv.GetRelay())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rv.Peer, err = peer.IDFromBytes(pbrv.GetPeer())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rv.Expiration = time.Unix(int64(pbrv.GetExpiration()), 0)
|
||||
return nil
|
||||
}
|
||||
17
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/acl.go
generated
vendored
Normal file
17
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/acl.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// ACLFilter is an Access Control mechanism for relayed connect.
|
||||
type ACLFilter interface {
|
||||
// AllowReserve returns true if a reservation from a peer with the given peer ID and multiaddr
|
||||
// is allowed.
|
||||
AllowReserve(p peer.ID, a ma.Multiaddr) bool
|
||||
// AllowConnect returns true if a source peer, with a given multiaddr is allowed to connect
|
||||
// to a destination peer.
|
||||
AllowConnect(src peer.ID, srcAddr ma.Multiaddr, dest peer.ID) bool
|
||||
}
|
||||
128
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go
generated
vendored
Normal file
128
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
asnutil "github.com/libp2p/go-libp2p-asn-util"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
var validity = 30 * time.Minute
|
||||
|
||||
var (
|
||||
errTooManyReservations = errors.New("too many reservations")
|
||||
errTooManyReservationsForPeer = errors.New("too many reservations for peer")
|
||||
errTooManyReservationsForIP = errors.New("too many peers for IP address")
|
||||
errTooManyReservationsForASN = errors.New("too many peers for ASN")
|
||||
)
|
||||
|
||||
// constraints implements various reservation constraints
|
||||
type constraints struct {
|
||||
rc *Resources
|
||||
|
||||
mutex sync.Mutex
|
||||
total []time.Time
|
||||
peers map[peer.ID][]time.Time
|
||||
ips map[string][]time.Time
|
||||
asns map[string][]time.Time
|
||||
}
|
||||
|
||||
// newConstraints creates a new constraints object.
|
||||
// The methods are *not* thread-safe; an external lock must be held if synchronization
|
||||
// is required.
|
||||
func newConstraints(rc *Resources) *constraints {
|
||||
return &constraints{
|
||||
rc: rc,
|
||||
peers: make(map[peer.ID][]time.Time),
|
||||
ips: make(map[string][]time.Time),
|
||||
asns: make(map[string][]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
// AddReservation adds a reservation for a given peer with a given multiaddr.
|
||||
// If adding this reservation violates IP constraints, an error is returned.
|
||||
func (c *constraints) AddReservation(p peer.ID, a ma.Multiaddr) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
c.cleanup(now)
|
||||
|
||||
if len(c.total) >= c.rc.MaxReservations {
|
||||
return errTooManyReservations
|
||||
}
|
||||
|
||||
ip, err := manet.ToIP(a)
|
||||
if err != nil {
|
||||
return errors.New("no IP address associated with peer")
|
||||
}
|
||||
|
||||
peerReservations := c.peers[p]
|
||||
if len(peerReservations) >= c.rc.MaxReservationsPerPeer {
|
||||
return errTooManyReservationsForPeer
|
||||
}
|
||||
|
||||
ipReservations := c.ips[ip.String()]
|
||||
if len(ipReservations) >= c.rc.MaxReservationsPerIP {
|
||||
return errTooManyReservationsForIP
|
||||
}
|
||||
|
||||
var asnReservations []time.Time
|
||||
var asn string
|
||||
// Only public addresses have an ASN. Skip checking ASN for private addresses as
|
||||
// initialising the ASN store is a costly operation. Skipping this check reduces a lot of
|
||||
// flakiness in tests
|
||||
if ip.To4() == nil && manet.IsPublicAddr(a) {
|
||||
asn, _ = asnutil.Store.AsnForIPv6(ip)
|
||||
if asn != "" {
|
||||
asnReservations = c.asns[asn]
|
||||
if len(asnReservations) >= c.rc.MaxReservationsPerASN {
|
||||
return errTooManyReservationsForASN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expiry := now.Add(validity)
|
||||
c.total = append(c.total, expiry)
|
||||
|
||||
peerReservations = append(peerReservations, expiry)
|
||||
c.peers[p] = peerReservations
|
||||
|
||||
ipReservations = append(ipReservations, expiry)
|
||||
c.ips[ip.String()] = ipReservations
|
||||
|
||||
if asn != "" {
|
||||
asnReservations = append(asnReservations, expiry)
|
||||
c.asns[asn] = asnReservations
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *constraints) cleanupList(l []time.Time, now time.Time) []time.Time {
|
||||
var index int
|
||||
for i, t := range l {
|
||||
if t.After(now) {
|
||||
break
|
||||
}
|
||||
index = i + 1
|
||||
}
|
||||
return l[index:]
|
||||
}
|
||||
|
||||
func (c *constraints) cleanup(now time.Time) {
|
||||
c.total = c.cleanupList(c.total, now)
|
||||
for k, peerReservations := range c.peers {
|
||||
c.peers[k] = c.cleanupList(peerReservations, now)
|
||||
}
|
||||
for k, ipReservations := range c.ips {
|
||||
c.ips[k] = c.cleanupList(ipReservations, now)
|
||||
}
|
||||
for k, asnReservations := range c.asns {
|
||||
c.asns[k] = c.cleanupList(asnReservations, now)
|
||||
}
|
||||
}
|
||||
268
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/metrics.go
generated
vendored
Normal file
268
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/p2p/metricshelper"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const metricNamespace = "libp2p_relaysvc"
|
||||
|
||||
var (
|
||||
status = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "status",
|
||||
Help: "Relay Status",
|
||||
},
|
||||
)
|
||||
|
||||
reservationsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "reservations_total",
|
||||
Help: "Relay Reservation Request",
|
||||
},
|
||||
[]string{"type"},
|
||||
)
|
||||
reservationRequestResponseStatusTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "reservation_request_response_status_total",
|
||||
Help: "Relay Reservation Request Response Status",
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
reservationRejectionsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "reservation_rejections_total",
|
||||
Help: "Relay Reservation Rejected Reason",
|
||||
},
|
||||
[]string{"reason"},
|
||||
)
|
||||
|
||||
connectionsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "connections_total",
|
||||
Help: "Relay Connection Total",
|
||||
},
|
||||
[]string{"type"},
|
||||
)
|
||||
connectionRequestResponseStatusTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "connection_request_response_status_total",
|
||||
Help: "Relay Connection Request Status",
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
connectionRejectionsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "connection_rejections_total",
|
||||
Help: "Relay Connection Rejected Reason",
|
||||
},
|
||||
[]string{"reason"},
|
||||
)
|
||||
connectionDurationSeconds = prometheus.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "connection_duration_seconds",
|
||||
Help: "Relay Connection Duration",
|
||||
},
|
||||
)
|
||||
|
||||
dataTransferredBytesTotal = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "data_transferred_bytes_total",
|
||||
Help: "Bytes Transferred Total",
|
||||
},
|
||||
)
|
||||
|
||||
collectors = []prometheus.Collector{
|
||||
status,
|
||||
reservationsTotal,
|
||||
reservationRequestResponseStatusTotal,
|
||||
reservationRejectionsTotal,
|
||||
connectionsTotal,
|
||||
connectionRequestResponseStatusTotal,
|
||||
connectionRejectionsTotal,
|
||||
connectionDurationSeconds,
|
||||
dataTransferredBytesTotal,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
requestStatusOK = "ok"
|
||||
requestStatusRejected = "rejected"
|
||||
requestStatusError = "error"
|
||||
)
|
||||
|
||||
// MetricsTracer is the interface for tracking metrics for relay service
|
||||
type MetricsTracer interface {
|
||||
// RelayStatus tracks whether the service is currently active
|
||||
RelayStatus(enabled bool)
|
||||
|
||||
// ConnectionOpened tracks metrics on opening a relay connection
|
||||
ConnectionOpened()
|
||||
// ConnectionClosed tracks metrics on closing a relay connection
|
||||
ConnectionClosed(d time.Duration)
|
||||
// ConnectionRequestHandled tracks metrics on handling a relay connection request
|
||||
ConnectionRequestHandled(status pbv2.Status)
|
||||
|
||||
// ReservationAllowed tracks metrics on opening or renewing a relay reservation
|
||||
ReservationAllowed(isRenewal bool)
|
||||
// ReservationRequestClosed tracks metrics on closing a relay reservation
|
||||
ReservationClosed(cnt int)
|
||||
// ReservationRequestHandled tracks metrics on handling a relay reservation request
|
||||
ReservationRequestHandled(status pbv2.Status)
|
||||
|
||||
// BytesTransferred tracks the total bytes transferred by the relay service
|
||||
BytesTransferred(cnt int)
|
||||
}
|
||||
|
||||
type metricsTracer struct{}
|
||||
|
||||
var _ MetricsTracer = &metricsTracer{}
|
||||
|
||||
type metricsTracerSetting struct {
|
||||
reg prometheus.Registerer
|
||||
}
|
||||
|
||||
type MetricsTracerOption func(*metricsTracerSetting)
|
||||
|
||||
func WithRegisterer(reg prometheus.Registerer) MetricsTracerOption {
|
||||
return func(s *metricsTracerSetting) {
|
||||
if reg != nil {
|
||||
s.reg = reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricsTracer(opts ...MetricsTracerOption) MetricsTracer {
|
||||
setting := &metricsTracerSetting{reg: prometheus.DefaultRegisterer}
|
||||
for _, opt := range opts {
|
||||
opt(setting)
|
||||
}
|
||||
metricshelper.RegisterCollectors(setting.reg, collectors...)
|
||||
return &metricsTracer{}
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) RelayStatus(enabled bool) {
|
||||
if enabled {
|
||||
status.Set(1)
|
||||
} else {
|
||||
status.Set(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ConnectionOpened() {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
*tags = append(*tags, "opened")
|
||||
|
||||
connectionsTotal.WithLabelValues(*tags...).Add(1)
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ConnectionClosed(d time.Duration) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
*tags = append(*tags, "closed")
|
||||
|
||||
connectionsTotal.WithLabelValues(*tags...).Add(1)
|
||||
connectionDurationSeconds.Observe(d.Seconds())
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ConnectionRequestHandled(status pbv2.Status) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
respStatus := getResponseStatus(status)
|
||||
|
||||
*tags = append(*tags, respStatus)
|
||||
connectionRequestResponseStatusTotal.WithLabelValues(*tags...).Add(1)
|
||||
if respStatus == requestStatusRejected {
|
||||
*tags = (*tags)[:0]
|
||||
*tags = append(*tags, getRejectionReason(status))
|
||||
connectionRejectionsTotal.WithLabelValues(*tags...).Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ReservationAllowed(isRenewal bool) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
if isRenewal {
|
||||
*tags = append(*tags, "renewed")
|
||||
} else {
|
||||
*tags = append(*tags, "opened")
|
||||
}
|
||||
|
||||
reservationsTotal.WithLabelValues(*tags...).Add(1)
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ReservationClosed(cnt int) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
*tags = append(*tags, "closed")
|
||||
|
||||
reservationsTotal.WithLabelValues(*tags...).Add(float64(cnt))
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) ReservationRequestHandled(status pbv2.Status) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
respStatus := getResponseStatus(status)
|
||||
|
||||
*tags = append(*tags, respStatus)
|
||||
reservationRequestResponseStatusTotal.WithLabelValues(*tags...).Add(1)
|
||||
if respStatus == requestStatusRejected {
|
||||
*tags = (*tags)[:0]
|
||||
*tags = append(*tags, getRejectionReason(status))
|
||||
reservationRejectionsTotal.WithLabelValues(*tags...).Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) BytesTransferred(cnt int) {
|
||||
dataTransferredBytesTotal.Add(float64(cnt))
|
||||
}
|
||||
|
||||
func getResponseStatus(status pbv2.Status) string {
|
||||
responseStatus := "unknown"
|
||||
switch status {
|
||||
case pbv2.Status_RESERVATION_REFUSED,
|
||||
pbv2.Status_RESOURCE_LIMIT_EXCEEDED,
|
||||
pbv2.Status_PERMISSION_DENIED,
|
||||
pbv2.Status_NO_RESERVATION,
|
||||
pbv2.Status_MALFORMED_MESSAGE:
|
||||
|
||||
responseStatus = requestStatusRejected
|
||||
case pbv2.Status_UNEXPECTED_MESSAGE, pbv2.Status_CONNECTION_FAILED:
|
||||
responseStatus = requestStatusError
|
||||
case pbv2.Status_OK:
|
||||
responseStatus = requestStatusOK
|
||||
}
|
||||
return responseStatus
|
||||
}
|
||||
|
||||
func getRejectionReason(status pbv2.Status) string {
|
||||
reason := "unknown"
|
||||
switch status {
|
||||
case pbv2.Status_RESERVATION_REFUSED:
|
||||
reason = "ip constraint violation"
|
||||
case pbv2.Status_RESOURCE_LIMIT_EXCEEDED:
|
||||
reason = "resource limit exceeded"
|
||||
case pbv2.Status_PERMISSION_DENIED:
|
||||
reason = "permission denied"
|
||||
case pbv2.Status_NO_RESERVATION:
|
||||
reason = "no reservation"
|
||||
case pbv2.Status_MALFORMED_MESSAGE:
|
||||
reason = "malformed message"
|
||||
}
|
||||
return reason
|
||||
}
|
||||
43
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/options.go
generated
vendored
Normal file
43
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/options.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package relay
|
||||
|
||||
type Option func(*Relay) error
|
||||
|
||||
// WithResources is a Relay option that sets specific relay resources for the relay.
|
||||
func WithResources(rc Resources) Option {
|
||||
return func(r *Relay) error {
|
||||
r.rc = rc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithLimit is a Relay option that sets only the relayed connection limits for the relay.
|
||||
func WithLimit(limit *RelayLimit) Option {
|
||||
return func(r *Relay) error {
|
||||
r.rc.Limit = limit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithInfiniteLimits is a Relay option that disables limits.
|
||||
func WithInfiniteLimits() Option {
|
||||
return func(r *Relay) error {
|
||||
r.rc.Limit = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithACL is a Relay option that supplies an ACLFilter for access control.
|
||||
func WithACL(acl ACLFilter) Option {
|
||||
return func(r *Relay) error {
|
||||
r.acl = acl
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetricsTracer is a Relay option that supplies a MetricsTracer for metrics
|
||||
func WithMetricsTracer(mt MetricsTracer) Option {
|
||||
return func(r *Relay) error {
|
||||
r.metricsTracer = mt
|
||||
return nil
|
||||
}
|
||||
}
|
||||
687
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/relay.go
generated
vendored
Normal file
687
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/relay.go
generated
vendored
Normal file
@@ -0,0 +1,687 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/record"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/proto"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
pool "github.com/libp2p/go-buffer-pool"
|
||||
asnutil "github.com/libp2p/go-libp2p-asn-util"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
const (
|
||||
ServiceName = "libp2p.relay/v2"
|
||||
|
||||
ReservationTagWeight = 10
|
||||
|
||||
StreamTimeout = time.Minute
|
||||
ConnectTimeout = 30 * time.Second
|
||||
HandshakeTimeout = time.Minute
|
||||
|
||||
relayHopTag = "relay-v2-hop"
|
||||
relayHopTagValue = 2
|
||||
|
||||
maxMessageSize = 4096
|
||||
)
|
||||
|
||||
var log = logging.Logger("relay")
|
||||
|
||||
// Relay is the (limited) relay service object.
|
||||
type Relay struct {
|
||||
ctx context.Context
|
||||
cancel func()
|
||||
|
||||
host host.Host
|
||||
rc Resources
|
||||
acl ACLFilter
|
||||
constraints *constraints
|
||||
scope network.ResourceScopeSpan
|
||||
notifiee network.Notifiee
|
||||
|
||||
mx sync.Mutex
|
||||
rsvp map[peer.ID]time.Time
|
||||
conns map[peer.ID]int
|
||||
closed bool
|
||||
|
||||
selfAddr ma.Multiaddr
|
||||
|
||||
metricsTracer MetricsTracer
|
||||
}
|
||||
|
||||
// New constructs a new limited relay that can provide relay services in the given host.
|
||||
func New(h host.Host, opts ...Option) (*Relay, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
r := &Relay{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
host: h,
|
||||
rc: DefaultResources(),
|
||||
acl: nil,
|
||||
rsvp: make(map[peer.ID]time.Time),
|
||||
conns: make(map[peer.ID]int),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error applying relay option: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// get a scope for memory reservations at service level
|
||||
err := h.Network().ResourceManager().ViewService(ServiceName,
|
||||
func(s network.ServiceScope) error {
|
||||
var err error
|
||||
r.scope, err = s.BeginSpan()
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.constraints = newConstraints(&r.rc)
|
||||
r.selfAddr = ma.StringCast(fmt.Sprintf("/p2p/%s", h.ID()))
|
||||
|
||||
h.SetStreamHandler(proto.ProtoIDv2Hop, r.handleStream)
|
||||
r.notifiee = &network.NotifyBundle{DisconnectedF: r.disconnected}
|
||||
h.Network().Notify(r.notifiee)
|
||||
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.RelayStatus(true)
|
||||
}
|
||||
go r.background()
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *Relay) Close() error {
|
||||
r.mx.Lock()
|
||||
if !r.closed {
|
||||
r.closed = true
|
||||
r.mx.Unlock()
|
||||
|
||||
r.host.RemoveStreamHandler(proto.ProtoIDv2Hop)
|
||||
r.host.Network().StopNotify(r.notifiee)
|
||||
r.scope.Done()
|
||||
r.cancel()
|
||||
r.gc()
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.RelayStatus(false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.mx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Relay) handleStream(s network.Stream) {
|
||||
log.Infof("new relay stream from: %s", s.Conn().RemotePeer())
|
||||
|
||||
if err := s.Scope().SetService(ServiceName); err != nil {
|
||||
log.Debugf("error attaching stream to relay service: %s", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.Scope().ReserveMemory(maxMessageSize, network.ReservationPriorityAlways); err != nil {
|
||||
log.Debugf("error reserving memory for stream: %s", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
defer s.Scope().ReleaseMemory(maxMessageSize)
|
||||
|
||||
rd := util.NewDelimitedReader(s, maxMessageSize)
|
||||
defer rd.Close()
|
||||
|
||||
s.SetReadDeadline(time.Now().Add(StreamTimeout))
|
||||
|
||||
var msg pbv2.HopMessage
|
||||
|
||||
err := rd.ReadMsg(&msg)
|
||||
if err != nil {
|
||||
r.handleError(s, pbv2.Status_MALFORMED_MESSAGE)
|
||||
return
|
||||
}
|
||||
// reset stream deadline as message has been read
|
||||
s.SetReadDeadline(time.Time{})
|
||||
switch msg.GetType() {
|
||||
case pbv2.HopMessage_RESERVE:
|
||||
status := r.handleReserve(s)
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ReservationRequestHandled(status)
|
||||
}
|
||||
case pbv2.HopMessage_CONNECT:
|
||||
status := r.handleConnect(s, &msg)
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ConnectionRequestHandled(status)
|
||||
}
|
||||
default:
|
||||
r.handleError(s, pbv2.Status_MALFORMED_MESSAGE)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) handleReserve(s network.Stream) pbv2.Status {
|
||||
defer s.Close()
|
||||
p := s.Conn().RemotePeer()
|
||||
a := s.Conn().RemoteMultiaddr()
|
||||
|
||||
if isRelayAddr(a) {
|
||||
log.Debugf("refusing relay reservation for %s; reservation attempt over relay connection")
|
||||
r.handleError(s, pbv2.Status_PERMISSION_DENIED)
|
||||
return pbv2.Status_PERMISSION_DENIED
|
||||
}
|
||||
|
||||
if r.acl != nil && !r.acl.AllowReserve(p, a) {
|
||||
log.Debugf("refusing relay reservation for %s; permission denied", p)
|
||||
r.handleError(s, pbv2.Status_PERMISSION_DENIED)
|
||||
return pbv2.Status_PERMISSION_DENIED
|
||||
}
|
||||
|
||||
r.mx.Lock()
|
||||
// Check if relay is still active. Otherwise ConnManager.UnTagPeer will not be called if this block runs after
|
||||
// Close() call
|
||||
if r.closed {
|
||||
r.mx.Unlock()
|
||||
log.Debugf("refusing relay reservation for %s; relay closed", p)
|
||||
r.handleError(s, pbv2.Status_PERMISSION_DENIED)
|
||||
return pbv2.Status_PERMISSION_DENIED
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
_, exists := r.rsvp[p]
|
||||
if !exists {
|
||||
if err := r.constraints.AddReservation(p, a); err != nil {
|
||||
r.mx.Unlock()
|
||||
log.Debugf("refusing relay reservation for %s; IP constraint violation: %s", p, err)
|
||||
r.handleError(s, pbv2.Status_RESERVATION_REFUSED)
|
||||
return pbv2.Status_RESERVATION_REFUSED
|
||||
}
|
||||
}
|
||||
|
||||
expire := now.Add(r.rc.ReservationTTL)
|
||||
r.rsvp[p] = expire
|
||||
r.host.ConnManager().TagPeer(p, "relay-reservation", ReservationTagWeight)
|
||||
r.mx.Unlock()
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ReservationAllowed(exists)
|
||||
}
|
||||
|
||||
log.Debugf("reserving relay slot for %s", p)
|
||||
|
||||
// Delivery of the reservation might fail for a number of reasons.
|
||||
// For example, the stream might be reset or the connection might be closed before the reservation is received.
|
||||
// In that case, the reservation will just be garbage collected later.
|
||||
if err := r.writeResponse(s, pbv2.Status_OK, r.makeReservationMsg(p, expire), r.makeLimitMsg(p)); err != nil {
|
||||
log.Debugf("error writing reservation response; retracting reservation for %s", p)
|
||||
s.Reset()
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
return pbv2.Status_OK
|
||||
}
|
||||
|
||||
func (r *Relay) handleConnect(s network.Stream, msg *pbv2.HopMessage) pbv2.Status {
|
||||
src := s.Conn().RemotePeer()
|
||||
a := s.Conn().RemoteMultiaddr()
|
||||
|
||||
span, err := r.scope.BeginSpan()
|
||||
if err != nil {
|
||||
log.Debugf("failed to begin relay transaction: %s", err)
|
||||
r.handleError(s, pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
fail := func(status pbv2.Status) {
|
||||
span.Done()
|
||||
r.handleError(s, status)
|
||||
}
|
||||
|
||||
// reserve buffers for the relay
|
||||
if err := span.ReserveMemory(2*r.rc.BufferSize, network.ReservationPriorityHigh); err != nil {
|
||||
log.Debugf("error reserving memory for relay: %s", err)
|
||||
fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
if isRelayAddr(a) {
|
||||
log.Debugf("refusing connection from %s; connection attempt over relay connection")
|
||||
fail(pbv2.Status_PERMISSION_DENIED)
|
||||
return pbv2.Status_PERMISSION_DENIED
|
||||
}
|
||||
|
||||
dest, err := util.PeerToPeerInfoV2(msg.GetPeer())
|
||||
if err != nil {
|
||||
fail(pbv2.Status_MALFORMED_MESSAGE)
|
||||
return pbv2.Status_MALFORMED_MESSAGE
|
||||
}
|
||||
|
||||
if r.acl != nil && !r.acl.AllowConnect(src, s.Conn().RemoteMultiaddr(), dest.ID) {
|
||||
log.Debugf("refusing connection from %s to %s; permission denied", src, dest.ID)
|
||||
fail(pbv2.Status_PERMISSION_DENIED)
|
||||
return pbv2.Status_PERMISSION_DENIED
|
||||
}
|
||||
|
||||
r.mx.Lock()
|
||||
_, rsvp := r.rsvp[dest.ID]
|
||||
if !rsvp {
|
||||
r.mx.Unlock()
|
||||
log.Debugf("refusing connection from %s to %s; no reservation", src, dest.ID)
|
||||
fail(pbv2.Status_NO_RESERVATION)
|
||||
return pbv2.Status_NO_RESERVATION
|
||||
}
|
||||
|
||||
srcConns := r.conns[src]
|
||||
if srcConns >= r.rc.MaxCircuits {
|
||||
r.mx.Unlock()
|
||||
log.Debugf("refusing connection from %s to %s; too many connections from %s", src, dest.ID, src)
|
||||
fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
destConns := r.conns[dest.ID]
|
||||
if destConns >= r.rc.MaxCircuits {
|
||||
r.mx.Unlock()
|
||||
log.Debugf("refusing connection from %s to %s; too many connecitons to %s", src, dest.ID, dest.ID)
|
||||
fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
r.addConn(src)
|
||||
r.addConn(dest.ID)
|
||||
r.mx.Unlock()
|
||||
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ConnectionOpened()
|
||||
}
|
||||
connStTime := time.Now()
|
||||
|
||||
cleanup := func() {
|
||||
span.Done()
|
||||
r.mx.Lock()
|
||||
r.rmConn(src)
|
||||
r.rmConn(dest.ID)
|
||||
r.mx.Unlock()
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ConnectionClosed(time.Since(connStTime))
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.ctx, ConnectTimeout)
|
||||
defer cancel()
|
||||
|
||||
ctx = network.WithNoDial(ctx, "relay connect")
|
||||
|
||||
bs, err := r.host.NewStream(ctx, dest.ID, proto.ProtoIDv2Stop)
|
||||
if err != nil {
|
||||
log.Debugf("error opening relay stream to %s: %s", dest.ID, err)
|
||||
cleanup()
|
||||
r.handleError(s, pbv2.Status_CONNECTION_FAILED)
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
fail = func(status pbv2.Status) {
|
||||
bs.Reset()
|
||||
cleanup()
|
||||
r.handleError(s, status)
|
||||
}
|
||||
|
||||
if err := bs.Scope().SetService(ServiceName); err != nil {
|
||||
log.Debugf("error attaching stream to relay service: %s", err)
|
||||
fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
|
||||
// handshake
|
||||
if err := bs.Scope().ReserveMemory(maxMessageSize, network.ReservationPriorityAlways); err != nil {
|
||||
log.Debugf("error reserving memory for stream: %s", err)
|
||||
fail(pbv2.Status_RESOURCE_LIMIT_EXCEEDED)
|
||||
return pbv2.Status_RESOURCE_LIMIT_EXCEEDED
|
||||
}
|
||||
defer bs.Scope().ReleaseMemory(maxMessageSize)
|
||||
|
||||
rd := util.NewDelimitedReader(bs, maxMessageSize)
|
||||
wr := util.NewDelimitedWriter(bs)
|
||||
defer rd.Close()
|
||||
|
||||
var stopmsg pbv2.StopMessage
|
||||
stopmsg.Type = pbv2.StopMessage_CONNECT.Enum()
|
||||
stopmsg.Peer = util.PeerInfoToPeerV2(peer.AddrInfo{ID: src})
|
||||
stopmsg.Limit = r.makeLimitMsg(dest.ID)
|
||||
|
||||
bs.SetDeadline(time.Now().Add(HandshakeTimeout))
|
||||
|
||||
err = wr.WriteMsg(&stopmsg)
|
||||
if err != nil {
|
||||
log.Debugf("error writing stop handshake")
|
||||
fail(pbv2.Status_CONNECTION_FAILED)
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
stopmsg.Reset()
|
||||
|
||||
err = rd.ReadMsg(&stopmsg)
|
||||
if err != nil {
|
||||
log.Debugf("error reading stop response: %s", err.Error())
|
||||
fail(pbv2.Status_CONNECTION_FAILED)
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
if t := stopmsg.GetType(); t != pbv2.StopMessage_STATUS {
|
||||
log.Debugf("unexpected stop response; not a status message (%d)", t)
|
||||
fail(pbv2.Status_CONNECTION_FAILED)
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
if status := stopmsg.GetStatus(); status != pbv2.Status_OK {
|
||||
log.Debugf("relay stop failure: %d", status)
|
||||
fail(pbv2.Status_CONNECTION_FAILED)
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
var response pbv2.HopMessage
|
||||
response.Type = pbv2.HopMessage_STATUS.Enum()
|
||||
response.Status = pbv2.Status_OK.Enum()
|
||||
response.Limit = r.makeLimitMsg(dest.ID)
|
||||
|
||||
wr = util.NewDelimitedWriter(s)
|
||||
err = wr.WriteMsg(&response)
|
||||
if err != nil {
|
||||
log.Debugf("error writing relay response: %s", err)
|
||||
bs.Reset()
|
||||
s.Reset()
|
||||
cleanup()
|
||||
return pbv2.Status_CONNECTION_FAILED
|
||||
}
|
||||
|
||||
// reset deadline
|
||||
bs.SetDeadline(time.Time{})
|
||||
|
||||
log.Infof("relaying connection from %s to %s", src, dest.ID)
|
||||
|
||||
var goroutines atomic.Int32
|
||||
goroutines.Store(2)
|
||||
|
||||
done := func() {
|
||||
if goroutines.Add(-1) == 0 {
|
||||
s.Close()
|
||||
bs.Close()
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
if r.rc.Limit != nil {
|
||||
deadline := time.Now().Add(r.rc.Limit.Duration)
|
||||
s.SetDeadline(deadline)
|
||||
bs.SetDeadline(deadline)
|
||||
go r.relayLimited(s, bs, src, dest.ID, r.rc.Limit.Data, done)
|
||||
go r.relayLimited(bs, s, dest.ID, src, r.rc.Limit.Data, done)
|
||||
} else {
|
||||
go r.relayUnlimited(s, bs, src, dest.ID, done)
|
||||
go r.relayUnlimited(bs, s, dest.ID, src, done)
|
||||
}
|
||||
|
||||
return pbv2.Status_OK
|
||||
}
|
||||
|
||||
func (r *Relay) addConn(p peer.ID) {
|
||||
conns := r.conns[p]
|
||||
conns++
|
||||
r.conns[p] = conns
|
||||
if conns == 1 {
|
||||
r.host.ConnManager().TagPeer(p, relayHopTag, relayHopTagValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) rmConn(p peer.ID) {
|
||||
conns := r.conns[p]
|
||||
conns--
|
||||
if conns > 0 {
|
||||
r.conns[p] = conns
|
||||
} else {
|
||||
delete(r.conns, p)
|
||||
r.host.ConnManager().UntagPeer(p, relayHopTag)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) relayLimited(src, dest network.Stream, srcID, destID peer.ID, limit int64, done func()) {
|
||||
defer done()
|
||||
|
||||
buf := pool.Get(r.rc.BufferSize)
|
||||
defer pool.Put(buf)
|
||||
|
||||
limitedSrc := io.LimitReader(src, limit)
|
||||
|
||||
count, err := r.copyWithBuffer(dest, limitedSrc, buf)
|
||||
if err != nil {
|
||||
log.Debugf("relay copy error: %s", err)
|
||||
// Reset both.
|
||||
src.Reset()
|
||||
dest.Reset()
|
||||
} else {
|
||||
// propagate the close
|
||||
dest.CloseWrite()
|
||||
if count == limit {
|
||||
// we've reached the limit, discard further input
|
||||
src.CloseRead()
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("relayed %d bytes from %s to %s", count, srcID, destID)
|
||||
}
|
||||
|
||||
func (r *Relay) relayUnlimited(src, dest network.Stream, srcID, destID peer.ID, done func()) {
|
||||
defer done()
|
||||
|
||||
buf := pool.Get(r.rc.BufferSize)
|
||||
defer pool.Put(buf)
|
||||
|
||||
count, err := r.copyWithBuffer(dest, src, buf)
|
||||
if err != nil {
|
||||
log.Debugf("relay copy error: %s", err)
|
||||
// Reset both.
|
||||
src.Reset()
|
||||
dest.Reset()
|
||||
} else {
|
||||
// propagate the close
|
||||
dest.CloseWrite()
|
||||
}
|
||||
|
||||
log.Debugf("relayed %d bytes from %s to %s", count, srcID, destID)
|
||||
}
|
||||
|
||||
// errInvalidWrite means that a write returned an impossible count.
|
||||
// copied from io.errInvalidWrite
|
||||
var errInvalidWrite = errors.New("invalid write result")
|
||||
|
||||
// copyWithBuffer copies from src to dst using the provided buf until either EOF is reached
|
||||
// on src or an error occurs. It reports the number of bytes transferred to metricsTracer.
|
||||
// The implementation is a modified form of io.CopyBuffer to support metrics tracking.
|
||||
func (r *Relay) copyWithBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw < 0 || nr < nw {
|
||||
nw = 0
|
||||
if ew == nil {
|
||||
ew = errInvalidWrite
|
||||
}
|
||||
}
|
||||
written += int64(nw)
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.BytesTransferred(nw)
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
func (r *Relay) handleError(s network.Stream, status pbv2.Status) {
|
||||
log.Debugf("relay error: %s (%d)", pbv2.Status_name[int32(status)], status)
|
||||
err := r.writeResponse(s, status, nil, nil)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
log.Debugf("error writing relay response: %s", err.Error())
|
||||
} else {
|
||||
s.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) writeResponse(s network.Stream, status pbv2.Status, rsvp *pbv2.Reservation, limit *pbv2.Limit) error {
|
||||
wr := util.NewDelimitedWriter(s)
|
||||
|
||||
var msg pbv2.HopMessage
|
||||
msg.Type = pbv2.HopMessage_STATUS.Enum()
|
||||
msg.Status = status.Enum()
|
||||
msg.Reservation = rsvp
|
||||
msg.Limit = limit
|
||||
|
||||
return wr.WriteMsg(&msg)
|
||||
}
|
||||
|
||||
func (r *Relay) makeReservationMsg(p peer.ID, expire time.Time) *pbv2.Reservation {
|
||||
expireUnix := uint64(expire.Unix())
|
||||
|
||||
var addrBytes [][]byte
|
||||
for _, addr := range r.host.Addrs() {
|
||||
if !manet.IsPublicAddr(addr) {
|
||||
continue
|
||||
}
|
||||
|
||||
addr = addr.Encapsulate(r.selfAddr)
|
||||
addrBytes = append(addrBytes, addr.Bytes())
|
||||
}
|
||||
|
||||
rsvp := &pbv2.Reservation{
|
||||
Expire: &expireUnix,
|
||||
Addrs: addrBytes,
|
||||
}
|
||||
|
||||
voucher := &proto.ReservationVoucher{
|
||||
Relay: r.host.ID(),
|
||||
Peer: p,
|
||||
Expiration: expire,
|
||||
}
|
||||
|
||||
envelope, err := record.Seal(voucher, r.host.Peerstore().PrivKey(r.host.ID()))
|
||||
if err != nil {
|
||||
log.Errorf("error sealing voucher for %s: %s", p, err)
|
||||
return rsvp
|
||||
}
|
||||
|
||||
blob, err := envelope.Marshal()
|
||||
if err != nil {
|
||||
log.Errorf("error marshalling voucher for %s: %s", p, err)
|
||||
return rsvp
|
||||
}
|
||||
|
||||
rsvp.Voucher = blob
|
||||
|
||||
return rsvp
|
||||
}
|
||||
|
||||
func (r *Relay) makeLimitMsg(p peer.ID) *pbv2.Limit {
|
||||
if r.rc.Limit == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
duration := uint32(r.rc.Limit.Duration / time.Second)
|
||||
data := uint64(r.rc.Limit.Data)
|
||||
|
||||
return &pbv2.Limit{
|
||||
Duration: &duration,
|
||||
Data: &data,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) background() {
|
||||
asnutil.Store.Init()
|
||||
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
r.gc()
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) gc() {
|
||||
r.mx.Lock()
|
||||
defer r.mx.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
cnt := 0
|
||||
for p, expire := range r.rsvp {
|
||||
if r.closed || expire.Before(now) {
|
||||
delete(r.rsvp, p)
|
||||
r.host.ConnManager().UntagPeer(p, "relay-reservation")
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
if r.metricsTracer != nil {
|
||||
r.metricsTracer.ReservationClosed(cnt)
|
||||
}
|
||||
|
||||
for p, count := range r.conns {
|
||||
if count == 0 {
|
||||
delete(r.conns, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Relay) disconnected(n network.Network, c network.Conn) {
|
||||
p := c.RemotePeer()
|
||||
if n.Connectedness(p) == network.Connected {
|
||||
return
|
||||
}
|
||||
|
||||
r.mx.Lock()
|
||||
_, ok := r.rsvp[p]
|
||||
if ok {
|
||||
delete(r.rsvp, p)
|
||||
}
|
||||
r.mx.Unlock()
|
||||
|
||||
if ok && r.metricsTracer != nil {
|
||||
r.metricsTracer.ReservationClosed(1)
|
||||
}
|
||||
}
|
||||
|
||||
func isRelayAddr(a ma.Multiaddr) bool {
|
||||
_, err := a.ValueForProtocol(ma.P_CIRCUIT)
|
||||
return err == nil
|
||||
}
|
||||
66
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/resources.go
generated
vendored
Normal file
66
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/resources.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Resources are the resource limits associated with the relay service.
|
||||
type Resources struct {
|
||||
// Limit is the (optional) relayed connection limits.
|
||||
Limit *RelayLimit
|
||||
|
||||
// ReservationTTL is the duration of a new (or refreshed reservation).
|
||||
// Defaults to 1hr.
|
||||
ReservationTTL time.Duration
|
||||
|
||||
// MaxReservations is the maximum number of active relay slots; defaults to 128.
|
||||
MaxReservations int
|
||||
// MaxCircuits is the maximum number of open relay connections for each peer; defaults to 16.
|
||||
MaxCircuits int
|
||||
// BufferSize is the size of the relayed connection buffers; defaults to 2048.
|
||||
BufferSize int
|
||||
|
||||
// MaxReservationsPerPeer is the maximum number of reservations originating from the same
|
||||
// peer; default is 4.
|
||||
MaxReservationsPerPeer int
|
||||
// MaxReservationsPerIP is the maximum number of reservations originating from the same
|
||||
// IP address; default is 8.
|
||||
MaxReservationsPerIP int
|
||||
// MaxReservationsPerASN is the maximum number of reservations origination from the same
|
||||
// ASN; default is 32
|
||||
MaxReservationsPerASN int
|
||||
}
|
||||
|
||||
// RelayLimit are the per relayed connection resource limits.
|
||||
type RelayLimit struct {
|
||||
// Duration is the time limit before resetting a relayed connection; defaults to 2min.
|
||||
Duration time.Duration
|
||||
// Data is the limit of data relayed (on each direction) before resetting the connection.
|
||||
// Defaults to 128KB
|
||||
Data int64
|
||||
}
|
||||
|
||||
// DefaultResources returns a Resources object with the default filled in.
|
||||
func DefaultResources() Resources {
|
||||
return Resources{
|
||||
Limit: DefaultLimit(),
|
||||
|
||||
ReservationTTL: time.Hour,
|
||||
|
||||
MaxReservations: 128,
|
||||
MaxCircuits: 16,
|
||||
BufferSize: 2048,
|
||||
|
||||
MaxReservationsPerPeer: 4,
|
||||
MaxReservationsPerIP: 8,
|
||||
MaxReservationsPerASN: 32,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultLimit returns a RelayLimit object with the defaults filled in.
|
||||
func DefaultLimit() *RelayLimit {
|
||||
return &RelayLimit{
|
||||
Duration: 2 * time.Minute,
|
||||
Data: 1 << 17, // 128K
|
||||
}
|
||||
}
|
||||
66
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util/io.go
generated
vendored
Normal file
66
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util/io.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
pool "github.com/libp2p/go-buffer-pool"
|
||||
"github.com/libp2p/go-msgio/pbio"
|
||||
"github.com/multiformats/go-varint"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type DelimitedReader struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// The gogo protobuf NewDelimitedReader is buffered, which may eat up stream data.
|
||||
// So we need to implement a compatible delimited reader that reads unbuffered.
|
||||
// There is a slowdown from unbuffered reading: when reading the message
|
||||
// it can take multiple single byte Reads to read the length and another Read
|
||||
// to read the message payload.
|
||||
// However, this is not critical performance degradation as
|
||||
// - the reader is utilized to read one (dialer, stop) or two messages (hop) during
|
||||
// the handshake, so it's a drop in the water for the connection lifetime.
|
||||
// - messages are small (max 4k) and the length fits in a couple of bytes,
|
||||
// so overall we have at most three reads per message.
|
||||
func NewDelimitedReader(r io.Reader, maxSize int) *DelimitedReader {
|
||||
return &DelimitedReader{r: r, buf: pool.Get(maxSize)}
|
||||
}
|
||||
|
||||
func (d *DelimitedReader) Close() {
|
||||
if d.buf != nil {
|
||||
pool.Put(d.buf)
|
||||
d.buf = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DelimitedReader) ReadByte() (byte, error) {
|
||||
buf := d.buf[:1]
|
||||
_, err := d.r.Read(buf)
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
func (d *DelimitedReader) ReadMsg(msg proto.Message) error {
|
||||
mlen, err := varint.ReadUvarint(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if uint64(len(d.buf)) < mlen {
|
||||
return errors.New("message too large")
|
||||
}
|
||||
|
||||
buf := d.buf[:mlen]
|
||||
_, err = io.ReadFull(d.r, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return proto.Unmarshal(buf, msg)
|
||||
}
|
||||
|
||||
func NewDelimitedWriter(w io.Writer) pbio.WriteCloser {
|
||||
return pbio.NewDelimitedWriter(w)
|
||||
}
|
||||
44
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util/pbconv.go
generated
vendored
Normal file
44
vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/util/pbconv.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
pbv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/pb"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
func PeerToPeerInfoV2(p *pbv2.Peer) (peer.AddrInfo, error) {
|
||||
if p == nil {
|
||||
return peer.AddrInfo{}, errors.New("nil peer")
|
||||
}
|
||||
|
||||
id, err := peer.IDFromBytes(p.Id)
|
||||
if err != nil {
|
||||
return peer.AddrInfo{}, err
|
||||
}
|
||||
|
||||
addrs := make([]ma.Multiaddr, 0, len(p.Addrs))
|
||||
|
||||
for _, addrBytes := range p.Addrs {
|
||||
a, err := ma.NewMultiaddrBytes(addrBytes)
|
||||
if err == nil {
|
||||
addrs = append(addrs, a)
|
||||
}
|
||||
}
|
||||
|
||||
return peer.AddrInfo{ID: id, Addrs: addrs}, nil
|
||||
}
|
||||
|
||||
func PeerInfoToPeerV2(pi peer.AddrInfo) *pbv2.Peer {
|
||||
addrs := make([][]byte, 0, len(pi.Addrs))
|
||||
for _, addr := range pi.Addrs {
|
||||
addrs = append(addrs, addr.Bytes())
|
||||
}
|
||||
|
||||
return &pbv2.Peer{
|
||||
Id: []byte(pi.ID),
|
||||
Addrs: addrs,
|
||||
}
|
||||
}
|
||||
27
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/filter.go
generated
vendored
Normal file
27
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/filter.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// WithAddrFilter is a Service option that enables multiaddress filtering.
|
||||
// It allows to only send a subset of observed addresses to the remote
|
||||
// peer. E.g., only announce TCP or QUIC multi addresses instead of both.
|
||||
// It also allows to only consider a subset of received multi addresses
|
||||
// that remote peers announced to us.
|
||||
// Theoretically, this API also allows to add multi addresses in both cases.
|
||||
func WithAddrFilter(f AddrFilter) Option {
|
||||
return func(hps *Service) error {
|
||||
hps.filter = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddrFilter defines the interface for the multi address filtering.
|
||||
type AddrFilter interface {
|
||||
// FilterLocal filters the multi addresses that are sent to the remote peer.
|
||||
FilterLocal(remoteID peer.ID, maddrs []ma.Multiaddr) []ma.Multiaddr
|
||||
// FilterRemote filters the multi addresses received from the remote peer.
|
||||
FilterRemote(remoteID peer.ID, maddrs []ma.Multiaddr) []ma.Multiaddr
|
||||
}
|
||||
289
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/holepuncher.go
generated
vendored
Normal file
289
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/holepuncher.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
||||
"github.com/libp2p/go-msgio/pbio"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
//go:generate protoc --proto_path=$PWD:$PWD/../../.. --go_out=. --go_opt=Mpb/holepunch.proto=./pb pb/holepunch.proto
|
||||
|
||||
// ErrHolePunchActive is returned from DirectConnect when another hole punching attempt is currently running
|
||||
var ErrHolePunchActive = errors.New("another hole punching attempt to this peer is active")
|
||||
|
||||
const (
|
||||
dialTimeout = 5 * time.Second
|
||||
maxRetries = 3
|
||||
)
|
||||
|
||||
// The holePuncher is run on the peer that's behind a NAT / Firewall.
|
||||
// It observes new incoming connections via a relay that it has a reservation with,
|
||||
// and initiates the DCUtR protocol with them.
|
||||
// It then first tries to establish a direct connection, and if that fails, it
|
||||
// initiates a hole punch.
|
||||
type holePuncher struct {
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
|
||||
host host.Host
|
||||
refCount sync.WaitGroup
|
||||
|
||||
ids identify.IDService
|
||||
|
||||
// active hole punches for deduplicating
|
||||
activeMx sync.Mutex
|
||||
active map[peer.ID]struct{}
|
||||
|
||||
closeMx sync.RWMutex
|
||||
closed bool
|
||||
|
||||
tracer *tracer
|
||||
filter AddrFilter
|
||||
}
|
||||
|
||||
func newHolePuncher(h host.Host, ids identify.IDService, tracer *tracer, filter AddrFilter) *holePuncher {
|
||||
hp := &holePuncher{
|
||||
host: h,
|
||||
ids: ids,
|
||||
active: make(map[peer.ID]struct{}),
|
||||
tracer: tracer,
|
||||
filter: filter,
|
||||
}
|
||||
hp.ctx, hp.ctxCancel = context.WithCancel(context.Background())
|
||||
h.Network().Notify((*netNotifiee)(hp))
|
||||
return hp
|
||||
}
|
||||
|
||||
func (hp *holePuncher) beginDirectConnect(p peer.ID) error {
|
||||
hp.closeMx.RLock()
|
||||
defer hp.closeMx.RUnlock()
|
||||
if hp.closed {
|
||||
return ErrClosed
|
||||
}
|
||||
|
||||
hp.activeMx.Lock()
|
||||
defer hp.activeMx.Unlock()
|
||||
if _, ok := hp.active[p]; ok {
|
||||
return ErrHolePunchActive
|
||||
}
|
||||
|
||||
hp.active[p] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DirectConnect attempts to make a direct connection with a remote peer.
|
||||
// It first attempts a direct dial (if we have a public address of that peer), and then
|
||||
// coordinates a hole punch over the given relay connection.
|
||||
func (hp *holePuncher) DirectConnect(p peer.ID) error {
|
||||
if err := hp.beginDirectConnect(p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
hp.activeMx.Lock()
|
||||
delete(hp.active, p)
|
||||
hp.activeMx.Unlock()
|
||||
}()
|
||||
|
||||
return hp.directConnect(p)
|
||||
}
|
||||
|
||||
func (hp *holePuncher) directConnect(rp peer.ID) error {
|
||||
// short-circuit check to see if we already have a direct connection
|
||||
if getDirectConnection(hp.host, rp) != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// short-circuit hole punching if a direct dial works.
|
||||
// attempt a direct connection ONLY if we have a public address for the remote peer
|
||||
for _, a := range hp.host.Peerstore().Addrs(rp) {
|
||||
if manet.IsPublicAddr(a) && !isRelayAddress(a) {
|
||||
forceDirectConnCtx := network.WithForceDirectDial(hp.ctx, "hole-punching")
|
||||
dialCtx, cancel := context.WithTimeout(forceDirectConnCtx, dialTimeout)
|
||||
|
||||
tstart := time.Now()
|
||||
// This dials *all* public addresses from the peerstore.
|
||||
err := hp.host.Connect(dialCtx, peer.AddrInfo{ID: rp})
|
||||
dt := time.Since(tstart)
|
||||
cancel()
|
||||
|
||||
if err != nil {
|
||||
hp.tracer.DirectDialFailed(rp, dt, err)
|
||||
break
|
||||
}
|
||||
hp.tracer.DirectDialSuccessful(rp, dt)
|
||||
log.Debugw("direct connection to peer successful, no need for a hole punch", "peer", rp)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugw("got inbound proxy conn", "peer", rp)
|
||||
|
||||
// hole punch
|
||||
for i := 1; i <= maxRetries; i++ {
|
||||
addrs, obsAddrs, rtt, err := hp.initiateHolePunch(rp)
|
||||
if err != nil {
|
||||
log.Debugw("hole punching failed", "peer", rp, "error", err)
|
||||
hp.tracer.ProtocolError(rp, err)
|
||||
return err
|
||||
}
|
||||
synTime := rtt / 2
|
||||
log.Debugf("peer RTT is %s; starting hole punch in %s", rtt, synTime)
|
||||
|
||||
// wait for sync to reach the other peer and then punch a hole for it in our NAT
|
||||
// by attempting a connect to it.
|
||||
timer := time.NewTimer(synTime)
|
||||
select {
|
||||
case start := <-timer.C:
|
||||
pi := peer.AddrInfo{
|
||||
ID: rp,
|
||||
Addrs: addrs,
|
||||
}
|
||||
hp.tracer.StartHolePunch(rp, addrs, rtt)
|
||||
hp.tracer.HolePunchAttempt(pi.ID)
|
||||
err := holePunchConnect(hp.ctx, hp.host, pi, true)
|
||||
dt := time.Since(start)
|
||||
hp.tracer.EndHolePunch(rp, dt, err)
|
||||
if err == nil {
|
||||
log.Debugw("hole punching with successful", "peer", rp, "time", dt)
|
||||
hp.tracer.HolePunchFinished("initiator", i, addrs, obsAddrs, getDirectConnection(hp.host, rp))
|
||||
return nil
|
||||
}
|
||||
case <-hp.ctx.Done():
|
||||
timer.Stop()
|
||||
return hp.ctx.Err()
|
||||
}
|
||||
if i == maxRetries {
|
||||
hp.tracer.HolePunchFinished("initiator", maxRetries, addrs, obsAddrs, nil)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("all retries for hole punch with peer %s failed", rp)
|
||||
}
|
||||
|
||||
// initiateHolePunch opens a new hole punching coordination stream,
|
||||
// exchanges the addresses and measures the RTT.
|
||||
func (hp *holePuncher) initiateHolePunch(rp peer.ID) ([]ma.Multiaddr, []ma.Multiaddr, time.Duration, error) {
|
||||
hpCtx := network.WithUseTransient(hp.ctx, "hole-punch")
|
||||
sCtx := network.WithNoDial(hpCtx, "hole-punch")
|
||||
|
||||
str, err := hp.host.NewStream(sCtx, rp, Protocol)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to open hole-punching stream: %w", err)
|
||||
}
|
||||
defer str.Close()
|
||||
|
||||
addr, obsAddr, rtt, err := hp.initiateHolePunchImpl(str)
|
||||
if err != nil {
|
||||
log.Debugf("%s", err)
|
||||
str.Reset()
|
||||
return addr, obsAddr, rtt, err
|
||||
}
|
||||
return addr, obsAddr, rtt, err
|
||||
}
|
||||
|
||||
func (hp *holePuncher) initiateHolePunchImpl(str network.Stream) ([]ma.Multiaddr, []ma.Multiaddr, time.Duration, error) {
|
||||
if err := str.Scope().SetService(ServiceName); err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("error attaching stream to holepunch service: %s", err)
|
||||
}
|
||||
|
||||
if err := str.Scope().ReserveMemory(maxMsgSize, network.ReservationPriorityAlways); err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("error reserving memory for stream: %s", err)
|
||||
}
|
||||
defer str.Scope().ReleaseMemory(maxMsgSize)
|
||||
|
||||
w := pbio.NewDelimitedWriter(str)
|
||||
rd := pbio.NewDelimitedReader(str, maxMsgSize)
|
||||
|
||||
str.SetDeadline(time.Now().Add(StreamTimeout))
|
||||
|
||||
// send a CONNECT and start RTT measurement.
|
||||
obsAddrs := removeRelayAddrs(hp.ids.OwnObservedAddrs())
|
||||
if hp.filter != nil {
|
||||
obsAddrs = hp.filter.FilterLocal(str.Conn().RemotePeer(), obsAddrs)
|
||||
}
|
||||
if len(obsAddrs) == 0 {
|
||||
return nil, nil, 0, errors.New("aborting hole punch initiation as we have no public address")
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
if err := w.WriteMsg(&pb.HolePunch{
|
||||
Type: pb.HolePunch_CONNECT.Enum(),
|
||||
ObsAddrs: addrsToBytes(obsAddrs),
|
||||
}); err != nil {
|
||||
str.Reset()
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
// wait for a CONNECT message from the remote peer
|
||||
var msg pb.HolePunch
|
||||
if err := rd.ReadMsg(&msg); err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to read CONNECT message from remote peer: %w", err)
|
||||
}
|
||||
rtt := time.Since(start)
|
||||
if t := msg.GetType(); t != pb.HolePunch_CONNECT {
|
||||
return nil, nil, 0, fmt.Errorf("expect CONNECT message, got %s", t)
|
||||
}
|
||||
|
||||
addrs := removeRelayAddrs(addrsFromBytes(msg.ObsAddrs))
|
||||
if hp.filter != nil {
|
||||
addrs = hp.filter.FilterRemote(str.Conn().RemotePeer(), addrs)
|
||||
}
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil, nil, 0, errors.New("didn't receive any public addresses in CONNECT")
|
||||
}
|
||||
|
||||
if err := w.WriteMsg(&pb.HolePunch{Type: pb.HolePunch_SYNC.Enum()}); err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("failed to send SYNC message for hole punching: %w", err)
|
||||
}
|
||||
return addrs, obsAddrs, rtt, nil
|
||||
}
|
||||
|
||||
func (hp *holePuncher) Close() error {
|
||||
hp.closeMx.Lock()
|
||||
hp.closed = true
|
||||
hp.closeMx.Unlock()
|
||||
hp.ctxCancel()
|
||||
hp.refCount.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
type netNotifiee holePuncher
|
||||
|
||||
func (nn *netNotifiee) Connected(_ network.Network, conn network.Conn) {
|
||||
hs := (*holePuncher)(nn)
|
||||
|
||||
// Hole punch if it's an inbound proxy connection.
|
||||
// If we already have a direct connection with the remote peer, this will be a no-op.
|
||||
if conn.Stat().Direction == network.DirInbound && isRelayAddress(conn.RemoteMultiaddr()) {
|
||||
hs.refCount.Add(1)
|
||||
go func() {
|
||||
defer hs.refCount.Done()
|
||||
|
||||
select {
|
||||
// waiting for Identify here will allow us to access the peer's public and observed addresses
|
||||
// that we can dial to for a hole punch.
|
||||
case <-hs.ids.IdentifyWait(conn):
|
||||
case <-hs.ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
_ = hs.DirectConnect(conn.RemotePeer())
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (nn *netNotifiee) Disconnected(_ network.Network, v network.Conn) {}
|
||||
func (nn *netNotifiee) Listen(n network.Network, a ma.Multiaddr) {}
|
||||
func (nn *netNotifiee) ListenClose(n network.Network, a ma.Multiaddr) {}
|
||||
187
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/metrics.go
generated
vendored
Normal file
187
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/p2p/metricshelper"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const metricNamespace = "libp2p_holepunch"
|
||||
|
||||
var (
|
||||
directDialsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "direct_dials_total",
|
||||
Help: "Direct Dials Total",
|
||||
},
|
||||
[]string{"outcome"},
|
||||
)
|
||||
hpAddressOutcomesTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "address_outcomes_total",
|
||||
Help: "Hole Punch outcomes by Transport",
|
||||
},
|
||||
[]string{"side", "num_attempts", "ipv", "transport", "outcome"},
|
||||
)
|
||||
hpOutcomesTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "outcomes_total",
|
||||
Help: "Hole Punch outcomes overall",
|
||||
},
|
||||
[]string{"side", "num_attempts", "outcome"},
|
||||
)
|
||||
|
||||
collectors = []prometheus.Collector{
|
||||
directDialsTotal,
|
||||
hpAddressOutcomesTotal,
|
||||
hpOutcomesTotal,
|
||||
}
|
||||
)
|
||||
|
||||
type MetricsTracer interface {
|
||||
HolePunchFinished(side string, attemptNum int, theirAddrs []ma.Multiaddr, ourAddr []ma.Multiaddr, directConn network.ConnMultiaddrs)
|
||||
DirectDialFinished(success bool)
|
||||
}
|
||||
|
||||
type metricsTracer struct{}
|
||||
|
||||
var _ MetricsTracer = &metricsTracer{}
|
||||
|
||||
type metricsTracerSetting struct {
|
||||
reg prometheus.Registerer
|
||||
}
|
||||
|
||||
type MetricsTracerOption func(*metricsTracerSetting)
|
||||
|
||||
func WithRegisterer(reg prometheus.Registerer) MetricsTracerOption {
|
||||
return func(s *metricsTracerSetting) {
|
||||
if reg != nil {
|
||||
s.reg = reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricsTracer(opts ...MetricsTracerOption) MetricsTracer {
|
||||
setting := &metricsTracerSetting{reg: prometheus.DefaultRegisterer}
|
||||
for _, opt := range opts {
|
||||
opt(setting)
|
||||
}
|
||||
metricshelper.RegisterCollectors(setting.reg, collectors...)
|
||||
// initialise metrics's labels so that the first data point is handled correctly
|
||||
for _, side := range []string{"initiator", "receiver"} {
|
||||
for _, numAttempts := range []string{"1", "2", "3", "4"} {
|
||||
for _, outcome := range []string{"success", "failed", "cancelled", "no_suitable_address"} {
|
||||
for _, ipv := range []string{"ip4", "ip6"} {
|
||||
for _, transport := range []string{"quic", "quic-v1", "tcp", "webtransport"} {
|
||||
hpAddressOutcomesTotal.WithLabelValues(side, numAttempts, ipv, transport, outcome)
|
||||
}
|
||||
}
|
||||
if outcome == "cancelled" {
|
||||
// not a valid outcome for the overall holepunch metric
|
||||
continue
|
||||
}
|
||||
hpOutcomesTotal.WithLabelValues(side, numAttempts, outcome)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &metricsTracer{}
|
||||
}
|
||||
|
||||
// HolePunchFinished tracks metrics completion of a holepunch. Metrics are tracked on
|
||||
// a holepunch attempt level and on individual addresses involved in a holepunch.
|
||||
//
|
||||
// outcome for an address is computed as:
|
||||
//
|
||||
// - success:
|
||||
// A direct connection was established with the peer using this address
|
||||
// - cancelled:
|
||||
// A direct connection was established with the peer but not using this address
|
||||
// - failed:
|
||||
// No direct connection was made to the peer and the peer reported an address
|
||||
// with the same transport as this address
|
||||
// - no_suitable_address:
|
||||
// The peer reported no address with the same transport as this address
|
||||
func (mt *metricsTracer) HolePunchFinished(side string, numAttempts int,
|
||||
remoteAddrs []ma.Multiaddr, localAddrs []ma.Multiaddr, directConn network.ConnMultiaddrs) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
*tags = append(*tags, side, getNumAttemptString(numAttempts))
|
||||
var dipv, dtransport string
|
||||
if directConn != nil {
|
||||
dipv = metricshelper.GetIPVersion(directConn.LocalMultiaddr())
|
||||
dtransport = metricshelper.GetTransport(directConn.LocalMultiaddr())
|
||||
}
|
||||
|
||||
matchingAddressCount := 0
|
||||
// calculate holepunch outcome for all the addresses involved
|
||||
for _, la := range localAddrs {
|
||||
lipv := metricshelper.GetIPVersion(la)
|
||||
ltransport := metricshelper.GetTransport(la)
|
||||
|
||||
matchingAddress := false
|
||||
for _, ra := range remoteAddrs {
|
||||
ripv := metricshelper.GetIPVersion(ra)
|
||||
rtransport := metricshelper.GetTransport(ra)
|
||||
if ripv == lipv && rtransport == ltransport {
|
||||
// the peer reported an address with the same transport
|
||||
matchingAddress = true
|
||||
matchingAddressCount++
|
||||
|
||||
*tags = append(*tags, ripv, rtransport)
|
||||
if directConn != nil && dipv == ripv && dtransport == rtransport {
|
||||
// the connection was made using this address
|
||||
*tags = append(*tags, "success")
|
||||
} else if directConn != nil {
|
||||
// connection was made but not using this address
|
||||
*tags = append(*tags, "cancelled")
|
||||
} else {
|
||||
// no connection was made
|
||||
*tags = append(*tags, "failed")
|
||||
}
|
||||
hpAddressOutcomesTotal.WithLabelValues(*tags...).Inc()
|
||||
*tags = (*tags)[:2] // 2 because we want to keep (side, numAttempts)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matchingAddress {
|
||||
*tags = append(*tags, lipv, ltransport, "no_suitable_address")
|
||||
hpAddressOutcomesTotal.WithLabelValues(*tags...).Inc()
|
||||
*tags = (*tags)[:2] // 2 because we want to keep (side, numAttempts)
|
||||
}
|
||||
}
|
||||
|
||||
outcome := "failed"
|
||||
if directConn != nil {
|
||||
outcome = "success"
|
||||
} else if matchingAddressCount == 0 {
|
||||
// there were no matching addresses, this attempt was going to fail
|
||||
outcome = "no_suitable_address"
|
||||
}
|
||||
|
||||
*tags = append(*tags, outcome)
|
||||
hpOutcomesTotal.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
func getNumAttemptString(numAttempt int) string {
|
||||
var attemptStr = [...]string{"0", "1", "2", "3", "4", "5"}
|
||||
if numAttempt > 5 {
|
||||
return "> 5"
|
||||
}
|
||||
return attemptStr[numAttempt]
|
||||
}
|
||||
|
||||
func (mt *metricsTracer) DirectDialFinished(success bool) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
if success {
|
||||
*tags = append(*tags, "success")
|
||||
} else {
|
||||
*tags = append(*tags, "failed")
|
||||
}
|
||||
directDialsTotal.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
215
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb/holepunch.pb.go
generated
vendored
Normal file
215
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb/holepunch.pb.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.21.12
|
||||
// source: pb/holepunch.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type HolePunch_Type int32
|
||||
|
||||
const (
|
||||
HolePunch_CONNECT HolePunch_Type = 100
|
||||
HolePunch_SYNC HolePunch_Type = 300
|
||||
)
|
||||
|
||||
// Enum value maps for HolePunch_Type.
|
||||
var (
|
||||
HolePunch_Type_name = map[int32]string{
|
||||
100: "CONNECT",
|
||||
300: "SYNC",
|
||||
}
|
||||
HolePunch_Type_value = map[string]int32{
|
||||
"CONNECT": 100,
|
||||
"SYNC": 300,
|
||||
}
|
||||
)
|
||||
|
||||
func (x HolePunch_Type) Enum() *HolePunch_Type {
|
||||
p := new(HolePunch_Type)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x HolePunch_Type) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (HolePunch_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_pb_holepunch_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (HolePunch_Type) Type() protoreflect.EnumType {
|
||||
return &file_pb_holepunch_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x HolePunch_Type) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func (x *HolePunch_Type) UnmarshalJSON(b []byte) error {
|
||||
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*x = HolePunch_Type(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use HolePunch_Type.Descriptor instead.
|
||||
func (HolePunch_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return file_pb_holepunch_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
// spec: https://github.com/libp2p/specs/blob/master/relay/DCUtR.md
|
||||
type HolePunch struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Type *HolePunch_Type `protobuf:"varint,1,req,name=type,enum=holepunch.pb.HolePunch_Type" json:"type,omitempty"`
|
||||
ObsAddrs [][]byte `protobuf:"bytes,2,rep,name=ObsAddrs" json:"ObsAddrs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HolePunch) Reset() {
|
||||
*x = HolePunch{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_holepunch_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HolePunch) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HolePunch) ProtoMessage() {}
|
||||
|
||||
func (x *HolePunch) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_holepunch_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HolePunch.ProtoReflect.Descriptor instead.
|
||||
func (*HolePunch) Descriptor() ([]byte, []int) {
|
||||
return file_pb_holepunch_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HolePunch) GetType() HolePunch_Type {
|
||||
if x != nil && x.Type != nil {
|
||||
return *x.Type
|
||||
}
|
||||
return HolePunch_CONNECT
|
||||
}
|
||||
|
||||
func (x *HolePunch) GetObsAddrs() [][]byte {
|
||||
if x != nil {
|
||||
return x.ObsAddrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_pb_holepunch_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pb_holepunch_proto_rawDesc = []byte{
|
||||
0x0a, 0x12, 0x70, 0x62, 0x2f, 0x68, 0x6f, 0x6c, 0x65, 0x70, 0x75, 0x6e, 0x63, 0x68, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x6f, 0x6c, 0x65, 0x70, 0x75, 0x6e, 0x63, 0x68, 0x2e,
|
||||
0x70, 0x62, 0x22, 0x79, 0x0a, 0x09, 0x48, 0x6f, 0x6c, 0x65, 0x50, 0x75, 0x6e, 0x63, 0x68, 0x12,
|
||||
0x30, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x1c, 0x2e,
|
||||
0x68, 0x6f, 0x6c, 0x65, 0x70, 0x75, 0x6e, 0x63, 0x68, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x6c,
|
||||
0x65, 0x50, 0x75, 0x6e, 0x63, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70,
|
||||
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4f, 0x62, 0x73, 0x41, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x08, 0x4f, 0x62, 0x73, 0x41, 0x64, 0x64, 0x72, 0x73, 0x22, 0x1e, 0x0a,
|
||||
0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54,
|
||||
0x10, 0x64, 0x12, 0x09, 0x0a, 0x04, 0x53, 0x59, 0x4e, 0x43, 0x10, 0xac, 0x02,
|
||||
}
|
||||
|
||||
var (
|
||||
file_pb_holepunch_proto_rawDescOnce sync.Once
|
||||
file_pb_holepunch_proto_rawDescData = file_pb_holepunch_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_pb_holepunch_proto_rawDescGZIP() []byte {
|
||||
file_pb_holepunch_proto_rawDescOnce.Do(func() {
|
||||
file_pb_holepunch_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_holepunch_proto_rawDescData)
|
||||
})
|
||||
return file_pb_holepunch_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_pb_holepunch_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_pb_holepunch_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_pb_holepunch_proto_goTypes = []interface{}{
|
||||
(HolePunch_Type)(0), // 0: holepunch.pb.HolePunch.Type
|
||||
(*HolePunch)(nil), // 1: holepunch.pb.HolePunch
|
||||
}
|
||||
var file_pb_holepunch_proto_depIdxs = []int32{
|
||||
0, // 0: holepunch.pb.HolePunch.type:type_name -> holepunch.pb.HolePunch.Type
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pb_holepunch_proto_init() }
|
||||
func file_pb_holepunch_proto_init() {
|
||||
if File_pb_holepunch_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_pb_holepunch_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HolePunch); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pb_holepunch_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_pb_holepunch_proto_goTypes,
|
||||
DependencyIndexes: file_pb_holepunch_proto_depIdxs,
|
||||
EnumInfos: file_pb_holepunch_proto_enumTypes,
|
||||
MessageInfos: file_pb_holepunch_proto_msgTypes,
|
||||
}.Build()
|
||||
File_pb_holepunch_proto = out.File
|
||||
file_pb_holepunch_proto_rawDesc = nil
|
||||
file_pb_holepunch_proto_goTypes = nil
|
||||
file_pb_holepunch_proto_depIdxs = nil
|
||||
}
|
||||
14
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb/holepunch.proto
generated
vendored
Normal file
14
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb/holepunch.proto
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package holepunch.pb;
|
||||
|
||||
// spec: https://github.com/libp2p/specs/blob/master/relay/DCUtR.md
|
||||
message HolePunch {
|
||||
enum Type {
|
||||
CONNECT = 100;
|
||||
SYNC = 300;
|
||||
}
|
||||
|
||||
required Type type=1;
|
||||
repeated bytes ObsAddrs = 2;
|
||||
}
|
||||
285
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/svc.go
generated
vendored
Normal file
285
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/svc.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/libp2p/go-libp2p/core/event"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch/pb"
|
||||
"github.com/libp2p/go-libp2p/p2p/protocol/identify"
|
||||
"github.com/libp2p/go-msgio/pbio"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// Protocol is the libp2p protocol for Hole Punching.
|
||||
const Protocol protocol.ID = "/libp2p/dcutr"
|
||||
|
||||
var log = logging.Logger("p2p-holepunch")
|
||||
|
||||
// StreamTimeout is the timeout for the hole punch protocol stream.
|
||||
var StreamTimeout = 1 * time.Minute
|
||||
|
||||
const (
|
||||
ServiceName = "libp2p.holepunch"
|
||||
|
||||
maxMsgSize = 4 * 1024 // 4K
|
||||
)
|
||||
|
||||
// ErrClosed is returned when the hole punching is closed
|
||||
var ErrClosed = errors.New("hole punching service closing")
|
||||
|
||||
type Option func(*Service) error
|
||||
|
||||
// The Service runs on every node that supports the DCUtR protocol.
|
||||
type Service struct {
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
|
||||
host host.Host
|
||||
ids identify.IDService
|
||||
|
||||
holePuncherMx sync.Mutex
|
||||
holePuncher *holePuncher
|
||||
|
||||
hasPublicAddrsChan chan struct{}
|
||||
|
||||
tracer *tracer
|
||||
filter AddrFilter
|
||||
|
||||
refCount sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewService creates a new service that can be used for hole punching
|
||||
// The Service runs on all hosts that support the DCUtR protocol,
|
||||
// no matter if they are behind a NAT / firewall or not.
|
||||
// The Service handles DCUtR streams (which are initiated from the node behind
|
||||
// a NAT / Firewall once we establish a connection to them through a relay.
|
||||
func NewService(h host.Host, ids identify.IDService, opts ...Option) (*Service, error) {
|
||||
if ids == nil {
|
||||
return nil, errors.New("identify service can't be nil")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
s := &Service{
|
||||
ctx: ctx,
|
||||
ctxCancel: cancel,
|
||||
host: h,
|
||||
ids: ids,
|
||||
hasPublicAddrsChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(s); err != nil {
|
||||
cancel()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
s.tracer.Start()
|
||||
|
||||
s.refCount.Add(1)
|
||||
go s.watchForPublicAddr()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Service) watchForPublicAddr() {
|
||||
defer s.refCount.Done()
|
||||
|
||||
log.Debug("waiting until we have at least one public address", "peer", s.host.ID())
|
||||
|
||||
// TODO: We should have an event here that fires when identify discovers a new
|
||||
// address (and when autonat confirms that address).
|
||||
// As we currently don't have an event like this, just check our observed addresses
|
||||
// regularly (exponential backoff starting at 250 ms, capped at 5s).
|
||||
duration := 250 * time.Millisecond
|
||||
const maxDuration = 5 * time.Second
|
||||
t := time.NewTimer(duration)
|
||||
defer t.Stop()
|
||||
for {
|
||||
if containsPublicAddr(s.ids.OwnObservedAddrs()) {
|
||||
log.Debug("Host now has a public address. Starting holepunch protocol.")
|
||||
s.host.SetStreamHandler(Protocol, s.handleNewStream)
|
||||
break
|
||||
}
|
||||
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-t.C:
|
||||
duration *= 2
|
||||
if duration > maxDuration {
|
||||
duration = maxDuration
|
||||
}
|
||||
t.Reset(duration)
|
||||
}
|
||||
}
|
||||
|
||||
// Only start the holePuncher if we're behind a NAT / firewall.
|
||||
sub, err := s.host.EventBus().Subscribe(&event.EvtLocalReachabilityChanged{}, eventbus.Name("holepunch"))
|
||||
if err != nil {
|
||||
log.Debugf("failed to subscripe to Reachability event: %s", err)
|
||||
return
|
||||
}
|
||||
defer sub.Close()
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case e, ok := <-sub.Out():
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if e.(event.EvtLocalReachabilityChanged).Reachability != network.ReachabilityPrivate {
|
||||
continue
|
||||
}
|
||||
s.holePuncherMx.Lock()
|
||||
s.holePuncher = newHolePuncher(s.host, s.ids, s.tracer, s.filter)
|
||||
s.holePuncherMx.Unlock()
|
||||
close(s.hasPublicAddrsChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the Hole Punch Service.
|
||||
func (s *Service) Close() error {
|
||||
var err error
|
||||
s.holePuncherMx.Lock()
|
||||
if s.holePuncher != nil {
|
||||
err = s.holePuncher.Close()
|
||||
}
|
||||
s.holePuncherMx.Unlock()
|
||||
s.tracer.Close()
|
||||
s.host.RemoveStreamHandler(Protocol)
|
||||
s.ctxCancel()
|
||||
s.refCount.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) incomingHolePunch(str network.Stream) (rtt time.Duration, remoteAddrs []ma.Multiaddr, ownAddrs []ma.Multiaddr, err error) {
|
||||
// sanity check: a hole punch request should only come from peers behind a relay
|
||||
if !isRelayAddress(str.Conn().RemoteMultiaddr()) {
|
||||
return 0, nil, nil, fmt.Errorf("received hole punch stream: %s", str.Conn().RemoteMultiaddr())
|
||||
}
|
||||
ownAddrs = removeRelayAddrs(s.ids.OwnObservedAddrs())
|
||||
if s.filter != nil {
|
||||
ownAddrs = s.filter.FilterLocal(str.Conn().RemotePeer(), ownAddrs)
|
||||
}
|
||||
|
||||
// If we can't tell the peer where to dial us, there's no point in starting the hole punching.
|
||||
if len(ownAddrs) == 0 {
|
||||
return 0, nil, nil, errors.New("rejecting hole punch request, as we don't have any public addresses")
|
||||
}
|
||||
|
||||
if err := str.Scope().ReserveMemory(maxMsgSize, network.ReservationPriorityAlways); err != nil {
|
||||
log.Debugf("error reserving memory for stream: %s, err")
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
defer str.Scope().ReleaseMemory(maxMsgSize)
|
||||
|
||||
wr := pbio.NewDelimitedWriter(str)
|
||||
rd := pbio.NewDelimitedReader(str, maxMsgSize)
|
||||
|
||||
// Read Connect message
|
||||
msg := new(pb.HolePunch)
|
||||
|
||||
str.SetDeadline(time.Now().Add(StreamTimeout))
|
||||
|
||||
if err := rd.ReadMsg(msg); err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("failed to read message from initator: %w", err)
|
||||
}
|
||||
if t := msg.GetType(); t != pb.HolePunch_CONNECT {
|
||||
return 0, nil, nil, fmt.Errorf("expected CONNECT message from initiator but got %d", t)
|
||||
}
|
||||
|
||||
obsDial := removeRelayAddrs(addrsFromBytes(msg.ObsAddrs))
|
||||
if s.filter != nil {
|
||||
obsDial = s.filter.FilterRemote(str.Conn().RemotePeer(), obsDial)
|
||||
}
|
||||
|
||||
log.Debugw("received hole punch request", "peer", str.Conn().RemotePeer(), "addrs", obsDial)
|
||||
if len(obsDial) == 0 {
|
||||
return 0, nil, nil, errors.New("expected CONNECT message to contain at least one address")
|
||||
}
|
||||
|
||||
// Write CONNECT message
|
||||
msg.Reset()
|
||||
msg.Type = pb.HolePunch_CONNECT.Enum()
|
||||
msg.ObsAddrs = addrsToBytes(ownAddrs)
|
||||
tstart := time.Now()
|
||||
if err := wr.WriteMsg(msg); err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("failed to write CONNECT message to initator: %w", err)
|
||||
}
|
||||
|
||||
// Read SYNC message
|
||||
msg.Reset()
|
||||
if err := rd.ReadMsg(msg); err != nil {
|
||||
return 0, nil, nil, fmt.Errorf("failed to read message from initator: %w", err)
|
||||
}
|
||||
if t := msg.GetType(); t != pb.HolePunch_SYNC {
|
||||
return 0, nil, nil, fmt.Errorf("expected SYNC message from initiator but got %d", t)
|
||||
}
|
||||
return time.Since(tstart), obsDial, ownAddrs, nil
|
||||
}
|
||||
|
||||
func (s *Service) handleNewStream(str network.Stream) {
|
||||
// Check directionality of the underlying connection.
|
||||
// Peer A receives an inbound connection from peer B.
|
||||
// Peer A opens a new hole punch stream to peer B.
|
||||
// Peer B receives this stream, calling this function.
|
||||
// Peer B sees the underlying connection as an outbound connection.
|
||||
if str.Conn().Stat().Direction == network.DirInbound {
|
||||
str.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
if err := str.Scope().SetService(ServiceName); err != nil {
|
||||
log.Debugf("error attaching stream to holepunch service: %s", err)
|
||||
str.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
rp := str.Conn().RemotePeer()
|
||||
rtt, addrs, ownAddrs, err := s.incomingHolePunch(str)
|
||||
if err != nil {
|
||||
s.tracer.ProtocolError(rp, err)
|
||||
log.Debugw("error handling holepunching stream from", "peer", rp, "error", err)
|
||||
str.Reset()
|
||||
return
|
||||
}
|
||||
str.Close()
|
||||
|
||||
// Hole punch now by forcing a connect
|
||||
pi := peer.AddrInfo{
|
||||
ID: rp,
|
||||
Addrs: addrs,
|
||||
}
|
||||
s.tracer.StartHolePunch(rp, addrs, rtt)
|
||||
log.Debugw("starting hole punch", "peer", rp)
|
||||
start := time.Now()
|
||||
s.tracer.HolePunchAttempt(pi.ID)
|
||||
err = holePunchConnect(s.ctx, s.host, pi, false)
|
||||
dt := time.Since(start)
|
||||
s.tracer.EndHolePunch(rp, dt, err)
|
||||
s.tracer.HolePunchFinished("receiver", 1, addrs, ownAddrs, getDirectConnection(s.host, rp))
|
||||
}
|
||||
|
||||
// DirectConnect is only exposed for testing purposes.
|
||||
// TODO: find a solution for this.
|
||||
func (s *Service) DirectConnect(p peer.ID) error {
|
||||
<-s.hasPublicAddrsChan
|
||||
s.holePuncherMx.Lock()
|
||||
holePuncher := s.holePuncher
|
||||
s.holePuncherMx.Unlock()
|
||||
return holePuncher.DirectConnect(p)
|
||||
}
|
||||
297
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/tracer.go
generated
vendored
Normal file
297
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/tracer.go
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
const (
|
||||
tracerGCInterval = 2 * time.Minute
|
||||
tracerCacheDuration = 5 * time.Minute
|
||||
)
|
||||
|
||||
// WithTracer enables holepunch tracing with EventTracer et
|
||||
func WithTracer(et EventTracer) Option {
|
||||
return func(hps *Service) error {
|
||||
hps.tracer = &tracer{
|
||||
et: et,
|
||||
mt: nil,
|
||||
self: hps.host.ID(),
|
||||
peers: make(map[peer.ID]struct {
|
||||
counter int
|
||||
last time.Time
|
||||
}),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetricsTracer enables holepunch Tracing with MetricsTracer mt
|
||||
func WithMetricsTracer(mt MetricsTracer) Option {
|
||||
return func(hps *Service) error {
|
||||
hps.tracer = &tracer{
|
||||
et: nil,
|
||||
mt: mt,
|
||||
self: hps.host.ID(),
|
||||
peers: make(map[peer.ID]struct {
|
||||
counter int
|
||||
last time.Time
|
||||
}),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMetricsAndEventTracer enables holepunch tracking with MetricsTracer and EventTracer
|
||||
func WithMetricsAndEventTracer(mt MetricsTracer, et EventTracer) Option {
|
||||
return func(hps *Service) error {
|
||||
hps.tracer = &tracer{
|
||||
et: et,
|
||||
mt: mt,
|
||||
self: hps.host.ID(),
|
||||
peers: make(map[peer.ID]struct {
|
||||
counter int
|
||||
last time.Time
|
||||
}),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type tracer struct {
|
||||
et EventTracer
|
||||
mt MetricsTracer
|
||||
self peer.ID
|
||||
|
||||
refCount sync.WaitGroup
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
|
||||
mutex sync.Mutex
|
||||
peers map[peer.ID]struct {
|
||||
counter int
|
||||
last time.Time
|
||||
}
|
||||
}
|
||||
|
||||
type EventTracer interface {
|
||||
Trace(evt *Event)
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Timestamp int64 // UNIX nanos
|
||||
Peer peer.ID // local peer ID
|
||||
Remote peer.ID // remote peer ID
|
||||
Type string // event type
|
||||
Evt interface{} // the actual event
|
||||
}
|
||||
|
||||
// Event Types
|
||||
const (
|
||||
DirectDialEvtT = "DirectDial"
|
||||
ProtocolErrorEvtT = "ProtocolError"
|
||||
StartHolePunchEvtT = "StartHolePunch"
|
||||
EndHolePunchEvtT = "EndHolePunch"
|
||||
HolePunchAttemptEvtT = "HolePunchAttempt"
|
||||
)
|
||||
|
||||
// Event Objects
|
||||
type DirectDialEvt struct {
|
||||
Success bool
|
||||
EllapsedTime time.Duration
|
||||
Error string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ProtocolErrorEvt struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
type StartHolePunchEvt struct {
|
||||
RemoteAddrs []string
|
||||
RTT time.Duration
|
||||
}
|
||||
|
||||
type EndHolePunchEvt struct {
|
||||
Success bool
|
||||
EllapsedTime time.Duration
|
||||
Error string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type HolePunchAttemptEvt struct {
|
||||
Attempt int
|
||||
}
|
||||
|
||||
// tracer interface
|
||||
func (t *tracer) DirectDialSuccessful(p peer.ID, dt time.Duration) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if t.et != nil {
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: DirectDialEvtT,
|
||||
Evt: &DirectDialEvt{
|
||||
Success: true,
|
||||
EllapsedTime: dt,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if t.mt != nil {
|
||||
t.mt.DirectDialFinished(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) DirectDialFailed(p peer.ID, dt time.Duration, err error) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if t.et != nil {
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: DirectDialEvtT,
|
||||
Evt: &DirectDialEvt{
|
||||
Success: false,
|
||||
EllapsedTime: dt,
|
||||
Error: err.Error(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if t.mt != nil {
|
||||
t.mt.DirectDialFinished(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) ProtocolError(p peer.ID, err error) {
|
||||
if t != nil && t.et != nil {
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: ProtocolErrorEvtT,
|
||||
Evt: &ProtocolErrorEvt{
|
||||
Error: err.Error(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) StartHolePunch(p peer.ID, obsAddrs []ma.Multiaddr, rtt time.Duration) {
|
||||
if t != nil && t.et != nil {
|
||||
addrs := make([]string, 0, len(obsAddrs))
|
||||
for _, a := range obsAddrs {
|
||||
addrs = append(addrs, a.String())
|
||||
}
|
||||
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: StartHolePunchEvtT,
|
||||
Evt: &StartHolePunchEvt{
|
||||
RemoteAddrs: addrs,
|
||||
RTT: rtt,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) EndHolePunch(p peer.ID, dt time.Duration, err error) {
|
||||
if t != nil && t.et != nil {
|
||||
evt := &EndHolePunchEvt{
|
||||
Success: err == nil,
|
||||
EllapsedTime: dt,
|
||||
}
|
||||
if err != nil {
|
||||
evt.Error = err.Error()
|
||||
}
|
||||
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: EndHolePunchEvtT,
|
||||
Evt: evt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) HolePunchFinished(side string, numAttempts int, theirAddrs []ma.Multiaddr, ourAddrs []ma.Multiaddr, directConn network.Conn) {
|
||||
if t != nil && t.mt != nil {
|
||||
t.mt.HolePunchFinished(side, numAttempts, theirAddrs, ourAddrs, directConn)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) HolePunchAttempt(p peer.ID) {
|
||||
if t != nil && t.et != nil {
|
||||
now := time.Now()
|
||||
t.mutex.Lock()
|
||||
attempt := t.peers[p]
|
||||
attempt.counter++
|
||||
counter := attempt.counter
|
||||
attempt.last = now
|
||||
t.peers[p] = attempt
|
||||
t.mutex.Unlock()
|
||||
|
||||
t.et.Trace(&Event{
|
||||
Timestamp: now.UnixNano(),
|
||||
Peer: t.self,
|
||||
Remote: p,
|
||||
Type: HolePunchAttemptEvtT,
|
||||
Evt: &HolePunchAttemptEvt{Attempt: counter},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// gc cleans up the peers map. This is only run when tracer is initialised with a non nil
|
||||
// EventTracer
|
||||
func (t *tracer) gc() {
|
||||
defer t.refCount.Done()
|
||||
timer := time.NewTicker(tracerGCInterval)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
now := time.Now()
|
||||
t.mutex.Lock()
|
||||
for id, entry := range t.peers {
|
||||
if entry.last.Before(now.Add(-tracerCacheDuration)) {
|
||||
delete(t.peers, id)
|
||||
}
|
||||
}
|
||||
t.mutex.Unlock()
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) Start() {
|
||||
if t != nil && t.et != nil {
|
||||
t.ctx, t.ctxCancel = context.WithCancel(context.Background())
|
||||
t.refCount.Add(1)
|
||||
go t.gc()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tracer) Close() error {
|
||||
if t != nil && t.et != nil {
|
||||
t.ctxCancel()
|
||||
t.refCount.Wait()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
79
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/util.go
generated
vendored
Normal file
79
vendor/github.com/libp2p/go-libp2p/p2p/protocol/holepunch/util.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package holepunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
func containsPublicAddr(addrs []ma.Multiaddr) bool {
|
||||
for _, addr := range addrs {
|
||||
if isRelayAddress(addr) || !manet.IsPublicAddr(addr) {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func removeRelayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
result := make([]ma.Multiaddr, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
if !isRelayAddress(addr) {
|
||||
result = append(result, addr)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func isRelayAddress(a ma.Multiaddr) bool {
|
||||
_, err := a.ValueForProtocol(ma.P_CIRCUIT)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func addrsToBytes(as []ma.Multiaddr) [][]byte {
|
||||
bzs := make([][]byte, 0, len(as))
|
||||
for _, a := range as {
|
||||
bzs = append(bzs, a.Bytes())
|
||||
}
|
||||
return bzs
|
||||
}
|
||||
|
||||
func addrsFromBytes(bzs [][]byte) []ma.Multiaddr {
|
||||
addrs := make([]ma.Multiaddr, 0, len(bzs))
|
||||
for _, bz := range bzs {
|
||||
a, err := ma.NewMultiaddrBytes(bz)
|
||||
if err == nil {
|
||||
addrs = append(addrs, a)
|
||||
}
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
func getDirectConnection(h host.Host, p peer.ID) network.Conn {
|
||||
for _, c := range h.Network().ConnsToPeer(p) {
|
||||
if !isRelayAddress(c.RemoteMultiaddr()) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func holePunchConnect(ctx context.Context, host host.Host, pi peer.AddrInfo, isClient bool) error {
|
||||
holePunchCtx := network.WithSimultaneousConnect(ctx, isClient, "hole-punching")
|
||||
forceDirectConnCtx := network.WithForceDirectDial(holePunchCtx, "hole-punching")
|
||||
dialCtx, cancel := context.WithTimeout(forceDirectConnCtx, dialTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := host.Connect(dialCtx, pi); err != nil {
|
||||
log.Debugw("hole punch attempt with peer failed", "peer ID", pi.ID, "error", err)
|
||||
return err
|
||||
}
|
||||
log.Debugw("hole punch successful", "peer", pi.ID)
|
||||
return nil
|
||||
}
|
||||
1002
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go
generated
vendored
Normal file
1002
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
206
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/metrics.go
generated
vendored
Normal file
206
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/metrics.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
package identify
|
||||
|
||||
import (
|
||||
"github.com/libp2p/go-libp2p/core/event"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/p2p/metricshelper"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const metricNamespace = "libp2p_identify"
|
||||
|
||||
var (
|
||||
pushesTriggered = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "identify_pushes_triggered_total",
|
||||
Help: "Pushes Triggered",
|
||||
},
|
||||
[]string{"trigger"},
|
||||
)
|
||||
identify = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "identify_total",
|
||||
Help: "Identify",
|
||||
},
|
||||
[]string{"dir"},
|
||||
)
|
||||
identifyPush = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "identify_push_total",
|
||||
Help: "Identify Push",
|
||||
},
|
||||
[]string{"dir"},
|
||||
)
|
||||
connPushSupportTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "conn_push_support_total",
|
||||
Help: "Identify Connection Push Support",
|
||||
},
|
||||
[]string{"support"},
|
||||
)
|
||||
protocolsCount = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "protocols_count",
|
||||
Help: "Protocols Count",
|
||||
},
|
||||
)
|
||||
addrsCount = prometheus.NewGauge(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "addrs_count",
|
||||
Help: "Address Count",
|
||||
},
|
||||
)
|
||||
numProtocolsReceived = prometheus.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "protocols_received",
|
||||
Help: "Number of Protocols received",
|
||||
Buckets: buckets,
|
||||
},
|
||||
)
|
||||
numAddrsReceived = prometheus.NewHistogram(
|
||||
prometheus.HistogramOpts{
|
||||
Namespace: metricNamespace,
|
||||
Name: "addrs_received",
|
||||
Help: "Number of addrs received",
|
||||
Buckets: buckets,
|
||||
},
|
||||
)
|
||||
collectors = []prometheus.Collector{
|
||||
pushesTriggered,
|
||||
identify,
|
||||
identifyPush,
|
||||
connPushSupportTotal,
|
||||
protocolsCount,
|
||||
addrsCount,
|
||||
numProtocolsReceived,
|
||||
numAddrsReceived,
|
||||
}
|
||||
// 1 to 20 and then up to 100 in steps of 5
|
||||
buckets = append(
|
||||
prometheus.LinearBuckets(1, 1, 20),
|
||||
prometheus.LinearBuckets(25, 5, 16)...,
|
||||
)
|
||||
)
|
||||
|
||||
type MetricsTracer interface {
|
||||
// TriggeredPushes counts IdentifyPushes triggered by event
|
||||
TriggeredPushes(event any)
|
||||
|
||||
// ConnPushSupport counts peers by Push Support
|
||||
ConnPushSupport(identifyPushSupport)
|
||||
|
||||
// IdentifyReceived tracks metrics on receiving an identify response
|
||||
IdentifyReceived(isPush bool, numProtocols int, numAddrs int)
|
||||
|
||||
// IdentifySent tracks metrics on sending an identify response
|
||||
IdentifySent(isPush bool, numProtocols int, numAddrs int)
|
||||
}
|
||||
|
||||
type metricsTracer struct{}
|
||||
|
||||
var _ MetricsTracer = &metricsTracer{}
|
||||
|
||||
type metricsTracerSetting struct {
|
||||
reg prometheus.Registerer
|
||||
}
|
||||
|
||||
type MetricsTracerOption func(*metricsTracerSetting)
|
||||
|
||||
func WithRegisterer(reg prometheus.Registerer) MetricsTracerOption {
|
||||
return func(s *metricsTracerSetting) {
|
||||
if reg != nil {
|
||||
s.reg = reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetricsTracer(opts ...MetricsTracerOption) MetricsTracer {
|
||||
setting := &metricsTracerSetting{reg: prometheus.DefaultRegisterer}
|
||||
for _, opt := range opts {
|
||||
opt(setting)
|
||||
}
|
||||
metricshelper.RegisterCollectors(setting.reg, collectors...)
|
||||
return &metricsTracer{}
|
||||
}
|
||||
|
||||
func (t *metricsTracer) TriggeredPushes(ev any) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
typ := "unknown"
|
||||
switch ev.(type) {
|
||||
case event.EvtLocalProtocolsUpdated:
|
||||
typ = "protocols_updated"
|
||||
case event.EvtLocalAddressesUpdated:
|
||||
typ = "addresses_updated"
|
||||
}
|
||||
*tags = append(*tags, typ)
|
||||
pushesTriggered.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
func (t *metricsTracer) IncrementPushSupport(s identifyPushSupport) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
*tags = append(*tags, getPushSupport(s))
|
||||
connPushSupportTotal.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
func (t *metricsTracer) IdentifySent(isPush bool, numProtocols int, numAddrs int) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
if isPush {
|
||||
*tags = append(*tags, metricshelper.GetDirection(network.DirOutbound))
|
||||
identifyPush.WithLabelValues(*tags...).Inc()
|
||||
} else {
|
||||
*tags = append(*tags, metricshelper.GetDirection(network.DirInbound))
|
||||
identify.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
protocolsCount.Set(float64(numProtocols))
|
||||
addrsCount.Set(float64(numAddrs))
|
||||
}
|
||||
|
||||
func (t *metricsTracer) IdentifyReceived(isPush bool, numProtocols int, numAddrs int) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
if isPush {
|
||||
*tags = append(*tags, metricshelper.GetDirection(network.DirInbound))
|
||||
identifyPush.WithLabelValues(*tags...).Inc()
|
||||
} else {
|
||||
*tags = append(*tags, metricshelper.GetDirection(network.DirOutbound))
|
||||
identify.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
numProtocolsReceived.Observe(float64(numProtocols))
|
||||
numAddrsReceived.Observe(float64(numAddrs))
|
||||
}
|
||||
|
||||
func (t *metricsTracer) ConnPushSupport(support identifyPushSupport) {
|
||||
tags := metricshelper.GetStringSlice()
|
||||
defer metricshelper.PutStringSlice(tags)
|
||||
|
||||
*tags = append(*tags, getPushSupport(support))
|
||||
connPushSupportTotal.WithLabelValues(*tags...).Inc()
|
||||
}
|
||||
|
||||
func getPushSupport(s identifyPushSupport) string {
|
||||
switch s {
|
||||
case identifyPushSupported:
|
||||
return "supported"
|
||||
case identifyPushUnsupported:
|
||||
return "not supported"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
640
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go
generated
vendored
Normal file
640
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go
generated
vendored
Normal file
@@ -0,0 +1,640 @@
|
||||
package identify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/event"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
// ActivationThresh sets how many times an address must be seen as "activated"
|
||||
// and therefore advertised to other peers as an address that the local peer
|
||||
// can be contacted on. The "seen" events expire by default after 40 minutes
|
||||
// (OwnObservedAddressTTL * ActivationThreshold). The are cleaned up during
|
||||
// the GC rounds set by GCInterval.
|
||||
var ActivationThresh = 4
|
||||
|
||||
// GCInterval specicies how often to make a round cleaning seen events and
|
||||
// observed addresses. An address will be cleaned if it has not been seen in
|
||||
// OwnObservedAddressTTL (10 minutes). A "seen" event will be cleaned up if
|
||||
// it is older than OwnObservedAddressTTL * ActivationThresh (40 minutes).
|
||||
var GCInterval = 10 * time.Minute
|
||||
|
||||
// observedAddrManagerWorkerChannelSize defines how many addresses can be enqueued
|
||||
// for adding to an ObservedAddrManager.
|
||||
var observedAddrManagerWorkerChannelSize = 16
|
||||
|
||||
// maxObservedAddrsPerIPAndTransport is the maximum number of observed addresses
|
||||
// we will return for each (IPx/TCP or UDP) group.
|
||||
var maxObservedAddrsPerIPAndTransport = 2
|
||||
|
||||
// observation records an address observation from an "observer" (where every IP
|
||||
// address is a unique observer).
|
||||
type observation struct {
|
||||
// seenTime is the last time this observation was made.
|
||||
seenTime time.Time
|
||||
// inbound indicates whether or not this observation has been made from
|
||||
// an inbound connection. This remains true even if we an observation
|
||||
// from a subsequent outbound connection.
|
||||
inbound bool
|
||||
}
|
||||
|
||||
// observedAddr is an entry for an address reported by our peers.
|
||||
// We only use addresses that:
|
||||
// - have been observed at least 4 times in last 40 minutes. (counter symmetric nats)
|
||||
// - have been observed at least once recently (10 minutes), because our position in the
|
||||
// network, or network port mapppings, may have changed.
|
||||
type observedAddr struct {
|
||||
addr ma.Multiaddr
|
||||
seenBy map[string]observation // peer(observer) address -> observation info
|
||||
lastSeen time.Time
|
||||
numInbound int
|
||||
}
|
||||
|
||||
func (oa *observedAddr) activated() bool {
|
||||
|
||||
// We only activate if other peers observed the same address
|
||||
// of ours at least 4 times. SeenBy peers are removed by GC if
|
||||
// they say the address more than ttl*ActivationThresh
|
||||
return len(oa.seenBy) >= ActivationThresh
|
||||
}
|
||||
|
||||
// GroupKey returns the group in which this observation belongs. Currently, an
|
||||
// observed address's group is just the address with all ports set to 0. This
|
||||
// means we can advertise the most commonly observed external ports without
|
||||
// advertising _every_ observed port.
|
||||
func (oa *observedAddr) groupKey() string {
|
||||
key := make([]byte, 0, len(oa.addr.Bytes()))
|
||||
ma.ForEach(oa.addr, func(c ma.Component) bool {
|
||||
switch proto := c.Protocol(); proto.Code {
|
||||
case ma.P_TCP, ma.P_UDP:
|
||||
key = append(key, proto.VCode...)
|
||||
key = append(key, 0, 0) // zero in two bytes
|
||||
default:
|
||||
key = append(key, c.Bytes()...)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
type newObservation struct {
|
||||
conn network.Conn
|
||||
observed ma.Multiaddr
|
||||
}
|
||||
|
||||
// ObservedAddrManager keeps track of a ObservedAddrs.
|
||||
type ObservedAddrManager struct {
|
||||
host host.Host
|
||||
|
||||
closeOnce sync.Once
|
||||
refCount sync.WaitGroup
|
||||
ctx context.Context // the context is canceled when Close is called
|
||||
ctxCancel context.CancelFunc
|
||||
|
||||
// latest observation from active connections
|
||||
// we'll "re-observe" these when we gc
|
||||
activeConnsMu sync.Mutex
|
||||
// active connection -> most recent observation
|
||||
activeConns map[network.Conn]ma.Multiaddr
|
||||
|
||||
mu sync.RWMutex
|
||||
closed bool
|
||||
// local(internal) address -> list of observed(external) addresses
|
||||
addrs map[string][]*observedAddr
|
||||
ttl time.Duration
|
||||
refreshTimer *time.Timer
|
||||
|
||||
// this is the worker channel
|
||||
wch chan newObservation
|
||||
|
||||
reachabilitySub event.Subscription
|
||||
reachability network.Reachability
|
||||
|
||||
currentUDPNATDeviceType network.NATDeviceType
|
||||
currentTCPNATDeviceType network.NATDeviceType
|
||||
emitNATDeviceTypeChanged event.Emitter
|
||||
}
|
||||
|
||||
// NewObservedAddrManager returns a new address manager using
|
||||
// peerstore.OwnObservedAddressTTL as the TTL.
|
||||
func NewObservedAddrManager(host host.Host) (*ObservedAddrManager, error) {
|
||||
oas := &ObservedAddrManager{
|
||||
addrs: make(map[string][]*observedAddr),
|
||||
ttl: peerstore.OwnObservedAddrTTL,
|
||||
wch: make(chan newObservation, observedAddrManagerWorkerChannelSize),
|
||||
host: host,
|
||||
activeConns: make(map[network.Conn]ma.Multiaddr),
|
||||
// refresh every ttl/2 so we don't forget observations from connected peers
|
||||
refreshTimer: time.NewTimer(peerstore.OwnObservedAddrTTL / 2),
|
||||
}
|
||||
oas.ctx, oas.ctxCancel = context.WithCancel(context.Background())
|
||||
|
||||
reachabilitySub, err := host.EventBus().Subscribe(new(event.EvtLocalReachabilityChanged), eventbus.Name("identify (obsaddr)"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to subscribe to reachability event: %s", err)
|
||||
}
|
||||
oas.reachabilitySub = reachabilitySub
|
||||
|
||||
emitter, err := host.EventBus().Emitter(new(event.EvtNATDeviceTypeChanged), eventbus.Stateful)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create emitter for NATDeviceType: %s", err)
|
||||
}
|
||||
oas.emitNATDeviceTypeChanged = emitter
|
||||
|
||||
oas.host.Network().Notify((*obsAddrNotifiee)(oas))
|
||||
oas.refCount.Add(1)
|
||||
go oas.worker()
|
||||
return oas, nil
|
||||
}
|
||||
|
||||
// AddrsFor return all activated observed addresses associated with the given
|
||||
// (resolved) listen address.
|
||||
func (oas *ObservedAddrManager) AddrsFor(addr ma.Multiaddr) (addrs []ma.Multiaddr) {
|
||||
oas.mu.RLock()
|
||||
defer oas.mu.RUnlock()
|
||||
|
||||
if len(oas.addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
observedAddrs, ok := oas.addrs[string(addr.Bytes())]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
return oas.filter(observedAddrs)
|
||||
}
|
||||
|
||||
// Addrs return all activated observed addresses
|
||||
func (oas *ObservedAddrManager) Addrs() []ma.Multiaddr {
|
||||
oas.mu.RLock()
|
||||
defer oas.mu.RUnlock()
|
||||
|
||||
if len(oas.addrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var allObserved []*observedAddr
|
||||
for _, addrs := range oas.addrs {
|
||||
allObserved = append(allObserved, addrs...)
|
||||
}
|
||||
return oas.filter(allObserved)
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) filter(observedAddrs []*observedAddr) []ma.Multiaddr {
|
||||
pmap := make(map[string][]*observedAddr)
|
||||
now := time.Now()
|
||||
|
||||
for i := range observedAddrs {
|
||||
a := observedAddrs[i]
|
||||
if now.Sub(a.lastSeen) <= oas.ttl && a.activated() {
|
||||
// group addresses by their IPX/Transport Protocol(TCP or UDP) pattern.
|
||||
pat := a.groupKey()
|
||||
pmap[pat] = append(pmap[pat], a)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
addrs := make([]ma.Multiaddr, 0, len(observedAddrs))
|
||||
for pat := range pmap {
|
||||
s := pmap[pat]
|
||||
|
||||
slices.SortFunc(s, func(first, second *observedAddr) int {
|
||||
// We prefer inbound connection observations over outbound.
|
||||
if first.numInbound > second.numInbound {
|
||||
return -1
|
||||
}
|
||||
// For ties, we prefer the ones with more votes.
|
||||
if first.numInbound == second.numInbound && len(first.seenBy) > len(second.seenBy) {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
|
||||
for i := 0; i < maxObservedAddrsPerIPAndTransport && i < len(s); i++ {
|
||||
addrs = append(addrs, s[i].addr)
|
||||
}
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
// Record records an address observation, if valid.
|
||||
func (oas *ObservedAddrManager) Record(conn network.Conn, observed ma.Multiaddr) {
|
||||
select {
|
||||
case oas.wch <- newObservation{
|
||||
conn: conn,
|
||||
observed: observed,
|
||||
}:
|
||||
default:
|
||||
log.Debugw("dropping address observation due to full buffer",
|
||||
"from", conn.RemoteMultiaddr(),
|
||||
"observed", observed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) worker() {
|
||||
defer oas.refCount.Done()
|
||||
|
||||
ticker := time.NewTicker(GCInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
subChan := oas.reachabilitySub.Out()
|
||||
for {
|
||||
select {
|
||||
case evt, ok := <-subChan:
|
||||
if !ok {
|
||||
subChan = nil
|
||||
continue
|
||||
}
|
||||
ev := evt.(event.EvtLocalReachabilityChanged)
|
||||
oas.reachability = ev.Reachability
|
||||
case obs := <-oas.wch:
|
||||
oas.maybeRecordObservation(obs.conn, obs.observed)
|
||||
case <-ticker.C:
|
||||
oas.gc()
|
||||
case <-oas.refreshTimer.C:
|
||||
oas.refresh()
|
||||
case <-oas.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) refresh() {
|
||||
oas.activeConnsMu.Lock()
|
||||
recycledObservations := make([]newObservation, 0, len(oas.activeConns))
|
||||
for conn, observed := range oas.activeConns {
|
||||
recycledObservations = append(recycledObservations, newObservation{
|
||||
conn: conn,
|
||||
observed: observed,
|
||||
})
|
||||
}
|
||||
oas.activeConnsMu.Unlock()
|
||||
|
||||
oas.mu.Lock()
|
||||
defer oas.mu.Unlock()
|
||||
for _, obs := range recycledObservations {
|
||||
oas.recordObservationUnlocked(obs.conn, obs.observed)
|
||||
}
|
||||
// refresh every ttl/2 so we don't forget observations from connected peers
|
||||
oas.refreshTimer.Reset(oas.ttl / 2)
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) gc() {
|
||||
oas.mu.Lock()
|
||||
defer oas.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for local, observedAddrs := range oas.addrs {
|
||||
filteredAddrs := observedAddrs[:0]
|
||||
for _, a := range observedAddrs {
|
||||
// clean up SeenBy set
|
||||
for k, ob := range a.seenBy {
|
||||
if now.Sub(ob.seenTime) > oas.ttl*time.Duration(ActivationThresh) {
|
||||
delete(a.seenBy, k)
|
||||
if ob.inbound {
|
||||
a.numInbound--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leave only alive observed addresses
|
||||
if now.Sub(a.lastSeen) <= oas.ttl {
|
||||
filteredAddrs = append(filteredAddrs, a)
|
||||
}
|
||||
}
|
||||
if len(filteredAddrs) > 0 {
|
||||
oas.addrs[local] = filteredAddrs
|
||||
} else {
|
||||
delete(oas.addrs, local)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) addConn(conn network.Conn, observed ma.Multiaddr) {
|
||||
oas.activeConnsMu.Lock()
|
||||
defer oas.activeConnsMu.Unlock()
|
||||
|
||||
// We need to make sure we haven't received a disconnect event for this
|
||||
// connection yet. The only way to do that right now is to make sure the
|
||||
// swarm still has the connection.
|
||||
//
|
||||
// Doing this under a lock that we _also_ take in a disconnect event
|
||||
// handler ensures everything happens in the right order.
|
||||
for _, c := range oas.host.Network().ConnsToPeer(conn.RemotePeer()) {
|
||||
if c == conn {
|
||||
oas.activeConns[conn] = observed
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) removeConn(conn network.Conn) {
|
||||
// DO NOT remove this lock.
|
||||
// This ensures we don't call addConn at the same time:
|
||||
// 1. see that we have a connection and pause inside addConn right before recording it.
|
||||
// 2. process a disconnect event.
|
||||
// 3. record the connection (leaking it).
|
||||
|
||||
oas.activeConnsMu.Lock()
|
||||
delete(oas.activeConns, conn)
|
||||
oas.activeConnsMu.Unlock()
|
||||
}
|
||||
|
||||
type normalizeMultiaddrer interface {
|
||||
NormalizeMultiaddr(addr ma.Multiaddr) ma.Multiaddr
|
||||
}
|
||||
|
||||
type addrsProvider interface {
|
||||
Addrs() []ma.Multiaddr
|
||||
}
|
||||
|
||||
type listenAddrsProvider interface {
|
||||
ListenAddresses() []ma.Multiaddr
|
||||
InterfaceListenAddresses() ([]ma.Multiaddr, error)
|
||||
}
|
||||
|
||||
func shouldRecordObservation(host addrsProvider, network listenAddrsProvider, conn network.ConnMultiaddrs, observed ma.Multiaddr) bool {
|
||||
// First, determine if this observation is even worth keeping...
|
||||
|
||||
// Ignore observations from loopback nodes. We already know our loopback
|
||||
// addresses.
|
||||
if manet.IsIPLoopback(observed) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Provided by NAT64 peers, these addresses are specific to the peer and not publicly routable
|
||||
if manet.IsNAT64IPv4ConvertedIPv6Addr(observed) {
|
||||
return false
|
||||
}
|
||||
|
||||
// we should only use ObservedAddr when our connection's LocalAddr is one
|
||||
// of our ListenAddrs. If we Dial out using an ephemeral addr, knowing that
|
||||
// address's external mapping is not very useful because the port will not be
|
||||
// the same as the listen addr.
|
||||
ifaceaddrs, err := network.InterfaceListenAddresses()
|
||||
if err != nil {
|
||||
log.Infof("failed to get interface listen addrs", err)
|
||||
return false
|
||||
}
|
||||
|
||||
normalizer, canNormalize := host.(normalizeMultiaddrer)
|
||||
|
||||
if canNormalize {
|
||||
for i, a := range ifaceaddrs {
|
||||
ifaceaddrs[i] = normalizer.NormalizeMultiaddr(a)
|
||||
}
|
||||
}
|
||||
|
||||
local := conn.LocalMultiaddr()
|
||||
if canNormalize {
|
||||
local = normalizer.NormalizeMultiaddr(local)
|
||||
}
|
||||
|
||||
listenAddrs := network.ListenAddresses()
|
||||
if canNormalize {
|
||||
for i, a := range listenAddrs {
|
||||
listenAddrs[i] = normalizer.NormalizeMultiaddr(a)
|
||||
}
|
||||
}
|
||||
|
||||
if !ma.Contains(ifaceaddrs, local) && !ma.Contains(listenAddrs, local) {
|
||||
// not in our list
|
||||
return false
|
||||
}
|
||||
|
||||
hostAddrs := host.Addrs()
|
||||
if canNormalize {
|
||||
for i, a := range hostAddrs {
|
||||
hostAddrs[i] = normalizer.NormalizeMultiaddr(a)
|
||||
}
|
||||
}
|
||||
|
||||
// We should reject the connection if the observation doesn't match the
|
||||
// transports of one of our advertised addresses.
|
||||
if !HasConsistentTransport(observed, hostAddrs) &&
|
||||
!HasConsistentTransport(observed, listenAddrs) {
|
||||
log.Debugw(
|
||||
"observed multiaddr doesn't match the transports of any announced addresses",
|
||||
"from", conn.RemoteMultiaddr(),
|
||||
"observed", observed,
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) maybeRecordObservation(conn network.Conn, observed ma.Multiaddr) {
|
||||
shouldRecord := shouldRecordObservation(oas.host, oas.host.Network(), conn, observed)
|
||||
if shouldRecord {
|
||||
// Ok, the observation is good, record it.
|
||||
log.Debugw("added own observed listen addr", "observed", observed)
|
||||
defer oas.addConn(conn, observed)
|
||||
|
||||
oas.mu.Lock()
|
||||
defer oas.mu.Unlock()
|
||||
oas.recordObservationUnlocked(conn, observed)
|
||||
|
||||
if oas.reachability == network.ReachabilityPrivate {
|
||||
oas.emitAllNATTypes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) recordObservationUnlocked(conn network.Conn, observed ma.Multiaddr) {
|
||||
now := time.Now()
|
||||
observerString := observerGroup(conn.RemoteMultiaddr())
|
||||
localString := string(conn.LocalMultiaddr().Bytes())
|
||||
ob := observation{
|
||||
seenTime: now,
|
||||
inbound: conn.Stat().Direction == network.DirInbound,
|
||||
}
|
||||
|
||||
// check if observed address seen yet, if so, update it
|
||||
for _, observedAddr := range oas.addrs[localString] {
|
||||
if observedAddr.addr.Equal(observed) {
|
||||
// Don't trump an outbound observation with an inbound
|
||||
// one.
|
||||
wasInbound := observedAddr.seenBy[observerString].inbound
|
||||
isInbound := ob.inbound
|
||||
ob.inbound = isInbound || wasInbound
|
||||
|
||||
if !wasInbound && isInbound {
|
||||
observedAddr.numInbound++
|
||||
}
|
||||
|
||||
observedAddr.seenBy[observerString] = ob
|
||||
observedAddr.lastSeen = now
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// observed address not seen yet, append it
|
||||
oa := &observedAddr{
|
||||
addr: observed,
|
||||
seenBy: map[string]observation{
|
||||
observerString: ob,
|
||||
},
|
||||
lastSeen: now,
|
||||
}
|
||||
if ob.inbound {
|
||||
oa.numInbound++
|
||||
}
|
||||
oas.addrs[localString] = append(oas.addrs[localString], oa)
|
||||
}
|
||||
|
||||
// For a given transport Protocol (TCP/UDP):
|
||||
//
|
||||
// 1. If we have an activated address, we are behind an Cone NAT.
|
||||
// With regards to RFC 3489, this could be either a Full Cone NAT, a Restricted Cone NAT or a
|
||||
// Port Restricted Cone NAT. However, we do NOT differentiate between them here and simply classify all such NATs as a Cone NAT.
|
||||
//
|
||||
// 2. If four different peers observe a different address for us on outbound connections, we
|
||||
// are MOST probably behind a Symmetric NAT.
|
||||
//
|
||||
// Please see the documentation on the enumerations for `network.NATDeviceType` for more details about these NAT Device types
|
||||
// and how they relate to NAT traversal via Hole Punching.
|
||||
func (oas *ObservedAddrManager) emitAllNATTypes() {
|
||||
var allObserved []*observedAddr
|
||||
for _, addrs := range oas.addrs {
|
||||
allObserved = append(allObserved, addrs...)
|
||||
}
|
||||
|
||||
hasChanged, natType := oas.emitSpecificNATType(allObserved, ma.P_TCP, network.NATTransportTCP, oas.currentTCPNATDeviceType)
|
||||
if hasChanged {
|
||||
oas.currentTCPNATDeviceType = natType
|
||||
}
|
||||
|
||||
hasChanged, natType = oas.emitSpecificNATType(allObserved, ma.P_UDP, network.NATTransportUDP, oas.currentUDPNATDeviceType)
|
||||
if hasChanged {
|
||||
oas.currentUDPNATDeviceType = natType
|
||||
}
|
||||
}
|
||||
|
||||
// returns true along with the new NAT device type if the NAT device type for the given protocol has changed.
|
||||
// returns false otherwise.
|
||||
func (oas *ObservedAddrManager) emitSpecificNATType(addrs []*observedAddr, protoCode int, transportProto network.NATTransportProtocol,
|
||||
currentNATType network.NATDeviceType) (bool, network.NATDeviceType) {
|
||||
now := time.Now()
|
||||
seenBy := make(map[string]struct{})
|
||||
cnt := 0
|
||||
|
||||
for _, oa := range addrs {
|
||||
_, err := oa.addr.ValueForProtocol(protoCode)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// if we have an activated addresses, it's a Cone NAT.
|
||||
if now.Sub(oa.lastSeen) <= oas.ttl && oa.activated() {
|
||||
if currentNATType != network.NATDeviceTypeCone {
|
||||
oas.emitNATDeviceTypeChanged.Emit(event.EvtNATDeviceTypeChanged{
|
||||
TransportProtocol: transportProto,
|
||||
NatDeviceType: network.NATDeviceTypeCone,
|
||||
})
|
||||
return true, network.NATDeviceTypeCone
|
||||
}
|
||||
|
||||
// our current NAT Device Type is already CONE, nothing to do here.
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// An observed address on an outbound connection that has ONLY been seen by one peer
|
||||
if now.Sub(oa.lastSeen) <= oas.ttl && oa.numInbound == 0 && len(oa.seenBy) == 1 {
|
||||
cnt++
|
||||
for s := range oa.seenBy {
|
||||
seenBy[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If four different peers observe a different address for us on each of four outbound connections, we
|
||||
// are MOST probably behind a Symmetric NAT.
|
||||
if cnt >= ActivationThresh && len(seenBy) >= ActivationThresh {
|
||||
if currentNATType != network.NATDeviceTypeSymmetric {
|
||||
oas.emitNATDeviceTypeChanged.Emit(event.EvtNATDeviceTypeChanged{
|
||||
TransportProtocol: transportProto,
|
||||
NatDeviceType: network.NATDeviceTypeSymmetric,
|
||||
})
|
||||
return true, network.NATDeviceTypeSymmetric
|
||||
}
|
||||
}
|
||||
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (oas *ObservedAddrManager) Close() error {
|
||||
oas.closeOnce.Do(func() {
|
||||
oas.ctxCancel()
|
||||
|
||||
oas.mu.Lock()
|
||||
oas.closed = true
|
||||
oas.refreshTimer.Stop()
|
||||
oas.mu.Unlock()
|
||||
|
||||
oas.refCount.Wait()
|
||||
oas.reachabilitySub.Close()
|
||||
oas.host.Network().StopNotify((*obsAddrNotifiee)(oas))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// observerGroup is a function that determines what part of
|
||||
// a multiaddr counts as a different observer. for example,
|
||||
// two ipfs nodes at the same IP/TCP transport would get
|
||||
// the exact same NAT mapping; they would count as the
|
||||
// same observer. This may protect against NATs who assign
|
||||
// different ports to addresses at different IP hosts, but
|
||||
// not TCP ports.
|
||||
//
|
||||
// Here, we use the root multiaddr address. This is mostly
|
||||
// IP addresses. In practice, this is what we want.
|
||||
func observerGroup(m ma.Multiaddr) string {
|
||||
// TODO: If IPv6 rolls out we should mark /64 routing zones as one group
|
||||
first, _ := ma.SplitFirst(m)
|
||||
return string(first.Bytes())
|
||||
}
|
||||
|
||||
// SetTTL sets the TTL of an observed address manager.
|
||||
func (oas *ObservedAddrManager) SetTTL(ttl time.Duration) {
|
||||
oas.mu.Lock()
|
||||
defer oas.mu.Unlock()
|
||||
if oas.closed {
|
||||
return
|
||||
}
|
||||
oas.ttl = ttl
|
||||
// refresh every ttl/2 so we don't forget observations from connected peers
|
||||
oas.refreshTimer.Reset(ttl / 2)
|
||||
}
|
||||
|
||||
// TTL gets the TTL of an observed address manager.
|
||||
func (oas *ObservedAddrManager) TTL() time.Duration {
|
||||
oas.mu.RLock()
|
||||
defer oas.mu.RUnlock()
|
||||
return oas.ttl
|
||||
}
|
||||
|
||||
type obsAddrNotifiee ObservedAddrManager
|
||||
|
||||
func (on *obsAddrNotifiee) Listen(n network.Network, a ma.Multiaddr) {}
|
||||
func (on *obsAddrNotifiee) ListenClose(n network.Network, a ma.Multiaddr) {}
|
||||
func (on *obsAddrNotifiee) Connected(n network.Network, v network.Conn) {}
|
||||
func (on *obsAddrNotifiee) Disconnected(n network.Network, v network.Conn) {
|
||||
(*ObservedAddrManager)(on).removeConn(v)
|
||||
}
|
||||
40
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/opts.go
generated
vendored
Normal file
40
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/opts.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package identify
|
||||
|
||||
type config struct {
|
||||
protocolVersion string
|
||||
userAgent string
|
||||
disableSignedPeerRecord bool
|
||||
metricsTracer MetricsTracer
|
||||
}
|
||||
|
||||
// Option is an option function for identify.
|
||||
type Option func(*config)
|
||||
|
||||
// ProtocolVersion sets the protocol version string that will be used to
|
||||
// identify the family of protocols used by the peer.
|
||||
func ProtocolVersion(s string) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.protocolVersion = s
|
||||
}
|
||||
}
|
||||
|
||||
// UserAgent sets the user agent this node will identify itself with to peers.
|
||||
func UserAgent(ua string) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.userAgent = ua
|
||||
}
|
||||
}
|
||||
|
||||
// DisableSignedPeerRecord disables populating signed peer records on the outgoing Identify response
|
||||
// and ONLY sends the unsigned addresses.
|
||||
func DisableSignedPeerRecord() Option {
|
||||
return func(cfg *config) {
|
||||
cfg.disableSignedPeerRecord = true
|
||||
}
|
||||
}
|
||||
|
||||
func WithMetricsTracer(tr MetricsTracer) Option {
|
||||
return func(cfg *config) {
|
||||
cfg.metricsTracer = tr
|
||||
}
|
||||
}
|
||||
219
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/pb/identify.pb.go
generated
vendored
Normal file
219
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/pb/identify.pb.go
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.30.0
|
||||
// protoc v3.21.12
|
||||
// source: pb/identify.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Identify struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// protocolVersion determines compatibility between peers
|
||||
ProtocolVersion *string `protobuf:"bytes,5,opt,name=protocolVersion" json:"protocolVersion,omitempty"` // e.g. ipfs/1.0.0
|
||||
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
|
||||
// includes the client name and client.
|
||||
AgentVersion *string `protobuf:"bytes,6,opt,name=agentVersion" json:"agentVersion,omitempty"` // e.g. go-ipfs/0.1.0
|
||||
// publicKey is this node's public key (which also gives its node.ID)
|
||||
// - may not need to be sent, as secure channel implies it has been sent.
|
||||
// - then again, if we change / disable secure channel, may still want it.
|
||||
PublicKey []byte `protobuf:"bytes,1,opt,name=publicKey" json:"publicKey,omitempty"`
|
||||
// listenAddrs are the multiaddrs the sender node listens for open connections on
|
||||
ListenAddrs [][]byte `protobuf:"bytes,2,rep,name=listenAddrs" json:"listenAddrs,omitempty"`
|
||||
// oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
|
||||
// this is useful information to convey to the other side, as it helps the remote endpoint
|
||||
// determine whether its connection to the local peer goes through NAT.
|
||||
ObservedAddr []byte `protobuf:"bytes,4,opt,name=observedAddr" json:"observedAddr,omitempty"`
|
||||
// protocols are the services this node is running
|
||||
Protocols []string `protobuf:"bytes,3,rep,name=protocols" json:"protocols,omitempty"`
|
||||
// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
|
||||
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
|
||||
// in a form that lets us share authenticated addrs with other peers.
|
||||
// see github.com/libp2p/go-libp2p/core/record/pb/envelope.proto and
|
||||
// github.com/libp2p/go-libp2p/core/peer/pb/peer_record.proto for message definitions.
|
||||
SignedPeerRecord []byte `protobuf:"bytes,8,opt,name=signedPeerRecord" json:"signedPeerRecord,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Identify) Reset() {
|
||||
*x = Identify{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_identify_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Identify) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Identify) ProtoMessage() {}
|
||||
|
||||
func (x *Identify) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_identify_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Identify.ProtoReflect.Descriptor instead.
|
||||
func (*Identify) Descriptor() ([]byte, []int) {
|
||||
return file_pb_identify_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Identify) GetProtocolVersion() string {
|
||||
if x != nil && x.ProtocolVersion != nil {
|
||||
return *x.ProtocolVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Identify) GetAgentVersion() string {
|
||||
if x != nil && x.AgentVersion != nil {
|
||||
return *x.AgentVersion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Identify) GetPublicKey() []byte {
|
||||
if x != nil {
|
||||
return x.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Identify) GetListenAddrs() [][]byte {
|
||||
if x != nil {
|
||||
return x.ListenAddrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Identify) GetObservedAddr() []byte {
|
||||
if x != nil {
|
||||
return x.ObservedAddr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Identify) GetProtocols() []string {
|
||||
if x != nil {
|
||||
return x.Protocols
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Identify) GetSignedPeerRecord() []byte {
|
||||
if x != nil {
|
||||
return x.SignedPeerRecord
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_pb_identify_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_pb_identify_proto_rawDesc = []byte{
|
||||
0x0a, 0x11, 0x70, 0x62, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x2e, 0x70, 0x62,
|
||||
0x22, 0x86, 0x02, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x12, 0x28, 0x0a,
|
||||
0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
||||
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61,
|
||||
0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x70,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
|
||||
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x6c, 0x69, 0x73,
|
||||
0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0b,
|
||||
0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x6f,
|
||||
0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0c, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x2a, 0x0a,
|
||||
0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72,
|
||||
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50,
|
||||
0x65, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
|
||||
}
|
||||
|
||||
var (
|
||||
file_pb_identify_proto_rawDescOnce sync.Once
|
||||
file_pb_identify_proto_rawDescData = file_pb_identify_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_pb_identify_proto_rawDescGZIP() []byte {
|
||||
file_pb_identify_proto_rawDescOnce.Do(func() {
|
||||
file_pb_identify_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_identify_proto_rawDescData)
|
||||
})
|
||||
return file_pb_identify_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_pb_identify_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_pb_identify_proto_goTypes = []interface{}{
|
||||
(*Identify)(nil), // 0: identify.pb.Identify
|
||||
}
|
||||
var file_pb_identify_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_pb_identify_proto_init() }
|
||||
func file_pb_identify_proto_init() {
|
||||
if File_pb_identify_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_pb_identify_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Identify); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pb_identify_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_pb_identify_proto_goTypes,
|
||||
DependencyIndexes: file_pb_identify_proto_depIdxs,
|
||||
MessageInfos: file_pb_identify_proto_msgTypes,
|
||||
}.Build()
|
||||
File_pb_identify_proto = out.File
|
||||
file_pb_identify_proto_rawDesc = nil
|
||||
file_pb_identify_proto_goTypes = nil
|
||||
file_pb_identify_proto_depIdxs = nil
|
||||
}
|
||||
36
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/pb/identify.proto
generated
vendored
Normal file
36
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/pb/identify.proto
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package identify.pb;
|
||||
|
||||
message Identify {
|
||||
|
||||
// protocolVersion determines compatibility between peers
|
||||
optional string protocolVersion = 5; // e.g. ipfs/1.0.0
|
||||
|
||||
// agentVersion is like a UserAgent string in browsers, or client version in bittorrent
|
||||
// includes the client name and client.
|
||||
optional string agentVersion = 6; // e.g. go-ipfs/0.1.0
|
||||
|
||||
// publicKey is this node's public key (which also gives its node.ID)
|
||||
// - may not need to be sent, as secure channel implies it has been sent.
|
||||
// - then again, if we change / disable secure channel, may still want it.
|
||||
optional bytes publicKey = 1;
|
||||
|
||||
// listenAddrs are the multiaddrs the sender node listens for open connections on
|
||||
repeated bytes listenAddrs = 2;
|
||||
|
||||
// oservedAddr is the multiaddr of the remote endpoint that the sender node perceives
|
||||
// this is useful information to convey to the other side, as it helps the remote endpoint
|
||||
// determine whether its connection to the local peer goes through NAT.
|
||||
optional bytes observedAddr = 4;
|
||||
|
||||
// protocols are the services this node is running
|
||||
repeated string protocols = 3;
|
||||
|
||||
// signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord,
|
||||
// signed by the sending node. It contains the same addresses as the listenAddrs field, but
|
||||
// in a form that lets us share authenticated addrs with other peers.
|
||||
// see github.com/libp2p/go-libp2p/core/record/pb/envelope.proto and
|
||||
// github.com/libp2p/go-libp2p/core/peer/pb/peer_record.proto for message definitions.
|
||||
optional bytes signedPeerRecord = 8;
|
||||
}
|
||||
43
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/user_agent.go
generated
vendored
Normal file
43
vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/user_agent.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package identify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
version := bi.Main.Version
|
||||
// version will only be non-empty if built as a dependency of another module
|
||||
if version == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if version != "(devel)" {
|
||||
defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, bi.Main.Version)
|
||||
return
|
||||
}
|
||||
|
||||
var revision string
|
||||
var dirty bool
|
||||
for _, bs := range bi.Settings {
|
||||
switch bs.Key {
|
||||
case "vcs.revision":
|
||||
revision = bs.Value
|
||||
if len(revision) > 9 {
|
||||
revision = revision[:9]
|
||||
}
|
||||
case "vcs.modified":
|
||||
if bs.Value == "true" {
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultUserAgent = fmt.Sprintf("%s@%s", bi.Main.Path, revision)
|
||||
if dirty {
|
||||
defaultUserAgent += "-dirty"
|
||||
}
|
||||
}
|
||||
202
vendor/github.com/libp2p/go-libp2p/p2p/protocol/ping/ping.go
generated
vendored
Normal file
202
vendor/github.com/libp2p/go-libp2p/p2p/protocol/ping/ping.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
package ping
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
mrand "math/rand"
|
||||
"time"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
pool "github.com/libp2p/go-buffer-pool"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
)
|
||||
|
||||
var log = logging.Logger("ping")
|
||||
|
||||
const (
|
||||
PingSize = 32
|
||||
pingTimeout = time.Second * 60
|
||||
|
||||
ID = "/ipfs/ping/1.0.0"
|
||||
|
||||
ServiceName = "libp2p.ping"
|
||||
)
|
||||
|
||||
type PingService struct {
|
||||
Host host.Host
|
||||
}
|
||||
|
||||
func NewPingService(h host.Host) *PingService {
|
||||
ps := &PingService{h}
|
||||
h.SetStreamHandler(ID, ps.PingHandler)
|
||||
return ps
|
||||
}
|
||||
|
||||
func (p *PingService) PingHandler(s network.Stream) {
|
||||
if err := s.Scope().SetService(ServiceName); err != nil {
|
||||
log.Debugf("error attaching stream to ping service: %s", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.Scope().ReserveMemory(PingSize, network.ReservationPriorityAlways); err != nil {
|
||||
log.Debugf("error reserving memory for ping stream: %s", err)
|
||||
s.Reset()
|
||||
return
|
||||
}
|
||||
defer s.Scope().ReleaseMemory(PingSize)
|
||||
|
||||
buf := pool.Get(PingSize)
|
||||
defer pool.Put(buf)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
defer close(errCh)
|
||||
timer := time.NewTimer(pingTimeout)
|
||||
defer timer.Stop()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-timer.C:
|
||||
log.Debug("ping timeout")
|
||||
case err, ok := <-errCh:
|
||||
if ok {
|
||||
log.Debug(err)
|
||||
} else {
|
||||
log.Error("ping loop failed without error")
|
||||
}
|
||||
}
|
||||
s.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
_, err := io.ReadFull(s, buf)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
_, err = s.Write(buf)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
timer.Reset(pingTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Result is a result of a ping attempt, either an RTT or an error.
|
||||
type Result struct {
|
||||
RTT time.Duration
|
||||
Error error
|
||||
}
|
||||
|
||||
func (ps *PingService) Ping(ctx context.Context, p peer.ID) <-chan Result {
|
||||
return Ping(ctx, ps.Host, p)
|
||||
}
|
||||
|
||||
func pingError(err error) chan Result {
|
||||
ch := make(chan Result, 1)
|
||||
ch <- Result{Error: err}
|
||||
close(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// Ping pings the remote peer until the context is canceled, returning a stream
|
||||
// of RTTs or errors.
|
||||
func Ping(ctx context.Context, h host.Host, p peer.ID) <-chan Result {
|
||||
s, err := h.NewStream(network.WithUseTransient(ctx, "ping"), p, ID)
|
||||
if err != nil {
|
||||
return pingError(err)
|
||||
}
|
||||
|
||||
if err := s.Scope().SetService(ServiceName); err != nil {
|
||||
log.Debugf("error attaching stream to ping service: %s", err)
|
||||
s.Reset()
|
||||
return pingError(err)
|
||||
}
|
||||
|
||||
b := make([]byte, 8)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
log.Errorf("failed to get cryptographic random: %s", err)
|
||||
s.Reset()
|
||||
return pingError(err)
|
||||
}
|
||||
ra := mrand.New(mrand.NewSource(int64(binary.BigEndian.Uint64(b))))
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
out := make(chan Result)
|
||||
go func() {
|
||||
defer close(out)
|
||||
defer cancel()
|
||||
|
||||
for ctx.Err() == nil {
|
||||
var res Result
|
||||
res.RTT, res.Error = ping(s, ra)
|
||||
|
||||
// canceled, ignore everything.
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// No error, record the RTT.
|
||||
if res.Error == nil {
|
||||
h.Peerstore().RecordLatency(p, res.RTT)
|
||||
}
|
||||
|
||||
select {
|
||||
case out <- res:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
// forces the ping to abort.
|
||||
<-ctx.Done()
|
||||
s.Reset()
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func ping(s network.Stream, randReader io.Reader) (time.Duration, error) {
|
||||
if err := s.Scope().ReserveMemory(2*PingSize, network.ReservationPriorityAlways); err != nil {
|
||||
log.Debugf("error reserving memory for ping stream: %s", err)
|
||||
s.Reset()
|
||||
return 0, err
|
||||
}
|
||||
defer s.Scope().ReleaseMemory(2 * PingSize)
|
||||
|
||||
buf := pool.Get(PingSize)
|
||||
defer pool.Put(buf)
|
||||
|
||||
if _, err := io.ReadFull(randReader, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
before := time.Now()
|
||||
if _, err := s.Write(buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rbuf := pool.Get(PingSize)
|
||||
defer pool.Put(rbuf)
|
||||
|
||||
if _, err := io.ReadFull(s, rbuf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, rbuf) {
|
||||
return 0, errors.New("ping packet was incorrect")
|
||||
}
|
||||
|
||||
return time.Since(before), nil
|
||||
}
|
||||
Reference in New Issue
Block a user