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:
91
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/conn.go
generated
vendored
Normal file
91
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/conn.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package libp2pquic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||
"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"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type conn struct {
|
||||
quicConn quic.Connection
|
||||
transport *transport
|
||||
scope network.ConnManagementScope
|
||||
|
||||
localPeer peer.ID
|
||||
localMultiaddr ma.Multiaddr
|
||||
|
||||
remotePeerID peer.ID
|
||||
remotePubKey ic.PubKey
|
||||
remoteMultiaddr ma.Multiaddr
|
||||
}
|
||||
|
||||
var _ tpt.CapableConn = &conn{}
|
||||
|
||||
// Close closes the connection.
|
||||
// It must be called even if the peer closed the connection in order for
|
||||
// garbage collection to properly work in this package.
|
||||
func (c *conn) Close() error {
|
||||
return c.closeWithError(0, "")
|
||||
}
|
||||
|
||||
func (c *conn) closeWithError(errCode quic.ApplicationErrorCode, errString string) error {
|
||||
c.transport.removeConn(c.quicConn)
|
||||
err := c.quicConn.CloseWithError(errCode, errString)
|
||||
c.scope.Done()
|
||||
return err
|
||||
}
|
||||
|
||||
// IsClosed returns whether a connection is fully closed.
|
||||
func (c *conn) IsClosed() bool {
|
||||
return c.quicConn.Context().Err() != nil
|
||||
}
|
||||
|
||||
func (c *conn) allowWindowIncrease(size uint64) bool {
|
||||
return c.scope.ReserveMemory(int(size), network.ReservationPriorityMedium) == nil
|
||||
}
|
||||
|
||||
// OpenStream creates a new stream.
|
||||
func (c *conn) OpenStream(ctx context.Context) (network.MuxedStream, error) {
|
||||
qstr, err := c.quicConn.OpenStreamSync(ctx)
|
||||
return &stream{Stream: qstr}, err
|
||||
}
|
||||
|
||||
// AcceptStream accepts a stream opened by the other side.
|
||||
func (c *conn) AcceptStream() (network.MuxedStream, error) {
|
||||
qstr, err := c.quicConn.AcceptStream(context.Background())
|
||||
return &stream{Stream: qstr}, err
|
||||
}
|
||||
|
||||
// LocalPeer returns our peer ID
|
||||
func (c *conn) LocalPeer() peer.ID { return c.localPeer }
|
||||
|
||||
// RemotePeer returns the peer ID of the remote peer.
|
||||
func (c *conn) RemotePeer() peer.ID { return c.remotePeerID }
|
||||
|
||||
// RemotePublicKey returns the public key of the remote peer.
|
||||
func (c *conn) RemotePublicKey() ic.PubKey { return c.remotePubKey }
|
||||
|
||||
// LocalMultiaddr returns the local Multiaddr associated
|
||||
func (c *conn) LocalMultiaddr() ma.Multiaddr { return c.localMultiaddr }
|
||||
|
||||
// RemoteMultiaddr returns the remote Multiaddr associated
|
||||
func (c *conn) RemoteMultiaddr() ma.Multiaddr { return c.remoteMultiaddr }
|
||||
|
||||
func (c *conn) Transport() tpt.Transport { return c.transport }
|
||||
|
||||
func (c *conn) Scope() network.ConnScope { return c.scope }
|
||||
|
||||
// ConnState is the state of security connection.
|
||||
func (c *conn) ConnState() network.ConnectionState {
|
||||
t := "quic-v1"
|
||||
if _, err := c.LocalMultiaddr().ValueForProtocol(ma.P_QUIC); err == nil {
|
||||
t = "quic"
|
||||
}
|
||||
return network.ConnectionState{Transport: t}
|
||||
}
|
||||
147
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go
generated
vendored
Normal file
147
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package libp2pquic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
tpt "github.com/libp2p/go-libp2p/core/transport"
|
||||
p2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
// A listener listens for QUIC connections.
|
||||
type listener struct {
|
||||
reuseListener quicreuse.Listener
|
||||
transport *transport
|
||||
rcmgr network.ResourceManager
|
||||
privKey ic.PrivKey
|
||||
localPeer peer.ID
|
||||
localMultiaddrs map[quic.VersionNumber]ma.Multiaddr
|
||||
}
|
||||
|
||||
func newListener(ln quicreuse.Listener, t *transport, localPeer peer.ID, key ic.PrivKey, rcmgr network.ResourceManager) (listener, error) {
|
||||
localMultiaddrs := make(map[quic.VersionNumber]ma.Multiaddr)
|
||||
for _, addr := range ln.Multiaddrs() {
|
||||
if _, err := addr.ValueForProtocol(ma.P_QUIC_V1); err == nil {
|
||||
localMultiaddrs[quic.Version1] = addr
|
||||
}
|
||||
}
|
||||
|
||||
return listener{
|
||||
reuseListener: ln,
|
||||
transport: t,
|
||||
rcmgr: rcmgr,
|
||||
privKey: key,
|
||||
localPeer: localPeer,
|
||||
localMultiaddrs: localMultiaddrs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Accept accepts new connections.
|
||||
func (l *listener) Accept() (tpt.CapableConn, error) {
|
||||
for {
|
||||
qconn, err := l.reuseListener.Accept(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := l.setupConn(qconn)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
l.transport.addConn(qconn, c)
|
||||
if l.transport.gater != nil && !(l.transport.gater.InterceptAccept(c) && l.transport.gater.InterceptSecured(network.DirInbound, c.remotePeerID, c)) {
|
||||
c.closeWithError(errorCodeConnectionGating, "connection gated")
|
||||
continue
|
||||
}
|
||||
|
||||
// return through active hole punching if any
|
||||
key := holePunchKey{addr: qconn.RemoteAddr().String(), peer: c.remotePeerID}
|
||||
var wasHolePunch bool
|
||||
l.transport.holePunchingMx.Lock()
|
||||
holePunch, ok := l.transport.holePunching[key]
|
||||
if ok && !holePunch.fulfilled {
|
||||
holePunch.connCh <- c
|
||||
wasHolePunch = true
|
||||
holePunch.fulfilled = true
|
||||
}
|
||||
l.transport.holePunchingMx.Unlock()
|
||||
if wasHolePunch {
|
||||
continue
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) setupConn(qconn quic.Connection) (*conn, error) {
|
||||
remoteMultiaddr, err := quicreuse.ToQuicMultiaddr(qconn.RemoteAddr(), qconn.ConnectionState().Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connScope, err := l.rcmgr.OpenConnection(network.DirInbound, false, remoteMultiaddr)
|
||||
if err != nil {
|
||||
log.Debugw("resource manager blocked incoming connection", "addr", qconn.RemoteAddr(), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
c, err := l.setupConnWithScope(qconn, connScope, remoteMultiaddr)
|
||||
if err != nil {
|
||||
connScope.Done()
|
||||
qconn.CloseWithError(1, "")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (l *listener) setupConnWithScope(qconn quic.Connection, connScope network.ConnManagementScope, remoteMultiaddr ma.Multiaddr) (*conn, error) {
|
||||
|
||||
// The tls.Config used to establish this connection already verified the certificate chain.
|
||||
// Since we don't have any way of knowing which tls.Config was used though,
|
||||
// we have to re-determine the peer's identity here.
|
||||
// Therefore, this is expected to never fail.
|
||||
remotePubKey, err := p2ptls.PubKeyFromCertChain(qconn.ConnectionState().TLS.PeerCertificates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remotePeerID, err := peer.IDFromPublicKey(remotePubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := connScope.SetPeer(remotePeerID); err != nil {
|
||||
log.Debugw("resource manager blocked incoming connection for peer", "peer", remotePeerID, "addr", qconn.RemoteAddr(), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localMultiaddr, found := l.localMultiaddrs[qconn.ConnectionState().Version]
|
||||
if !found {
|
||||
return nil, errors.New("unknown QUIC version:" + qconn.ConnectionState().Version.String())
|
||||
}
|
||||
|
||||
return &conn{
|
||||
quicConn: qconn,
|
||||
transport: l.transport,
|
||||
scope: connScope,
|
||||
localPeer: l.localPeer,
|
||||
localMultiaddr: localMultiaddr,
|
||||
remoteMultiaddr: remoteMultiaddr,
|
||||
remotePeerID: remotePeerID,
|
||||
remotePubKey: remotePubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
func (l *listener) Close() error {
|
||||
return l.reuseListener.Close()
|
||||
}
|
||||
|
||||
// Addr returns the address of this listener.
|
||||
func (l *listener) Addr() net.Addr {
|
||||
return l.reuseListener.Addr()
|
||||
}
|
||||
55
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/stream.go
generated
vendored
Normal file
55
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/stream.go
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
package libp2pquic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
const (
|
||||
reset quic.StreamErrorCode = 0
|
||||
)
|
||||
|
||||
type stream struct {
|
||||
quic.Stream
|
||||
}
|
||||
|
||||
var _ network.MuxedStream = &stream{}
|
||||
|
||||
func (s *stream) Read(b []byte) (n int, err error) {
|
||||
n, err = s.Stream.Read(b)
|
||||
if err != nil && errors.Is(err, &quic.StreamError{}) {
|
||||
err = network.ErrReset
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *stream) Write(b []byte) (n int, err error) {
|
||||
n, err = s.Stream.Write(b)
|
||||
if err != nil && errors.Is(err, &quic.StreamError{}) {
|
||||
err = network.ErrReset
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *stream) Reset() error {
|
||||
s.Stream.CancelRead(reset)
|
||||
s.Stream.CancelWrite(reset)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stream) Close() error {
|
||||
s.Stream.CancelRead(reset)
|
||||
return s.Stream.Close()
|
||||
}
|
||||
|
||||
func (s *stream) CloseRead() error {
|
||||
s.Stream.CancelRead(reset)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stream) CloseWrite() error {
|
||||
return s.Stream.Close()
|
||||
}
|
||||
399
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go
generated
vendored
Normal file
399
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go
generated
vendored
Normal file
@@ -0,0 +1,399 @@
|
||||
package libp2pquic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/connmgr"
|
||||
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/pnet"
|
||||
tpt "github.com/libp2p/go-libp2p/core/transport"
|
||||
p2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
mafmt "github.com/multiformats/go-multiaddr-fmt"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
var log = logging.Logger("quic-transport")
|
||||
|
||||
var ErrHolePunching = errors.New("hole punching attempted; no active dial")
|
||||
|
||||
var HolePunchTimeout = 5 * time.Second
|
||||
|
||||
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII
|
||||
|
||||
// The Transport implements the tpt.Transport interface for QUIC connections.
|
||||
type transport struct {
|
||||
privKey ic.PrivKey
|
||||
localPeer peer.ID
|
||||
identity *p2ptls.Identity
|
||||
connManager *quicreuse.ConnManager
|
||||
gater connmgr.ConnectionGater
|
||||
rcmgr network.ResourceManager
|
||||
|
||||
holePunchingMx sync.Mutex
|
||||
holePunching map[holePunchKey]*activeHolePunch
|
||||
|
||||
rndMx sync.Mutex
|
||||
rnd rand.Rand
|
||||
|
||||
connMx sync.Mutex
|
||||
conns map[quic.Connection]*conn
|
||||
|
||||
listenersMu sync.Mutex
|
||||
// map of UDPAddr as string to a virtualListeners
|
||||
listeners map[string][]*virtualListener
|
||||
}
|
||||
|
||||
var _ tpt.Transport = &transport{}
|
||||
|
||||
type holePunchKey struct {
|
||||
addr string
|
||||
peer peer.ID
|
||||
}
|
||||
|
||||
type activeHolePunch struct {
|
||||
connCh chan tpt.CapableConn
|
||||
fulfilled bool
|
||||
}
|
||||
|
||||
// NewTransport creates a new QUIC transport
|
||||
func NewTransport(key ic.PrivKey, connManager *quicreuse.ConnManager, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager) (tpt.Transport, error) {
|
||||
if len(psk) > 0 {
|
||||
log.Error("QUIC doesn't support private networks yet.")
|
||||
return nil, errors.New("QUIC doesn't support private networks yet")
|
||||
}
|
||||
localPeer, err := peer.IDFromPrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
identity, err := p2ptls.NewIdentity(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rcmgr == nil {
|
||||
rcmgr = &network.NullResourceManager{}
|
||||
}
|
||||
|
||||
return &transport{
|
||||
privKey: key,
|
||||
localPeer: localPeer,
|
||||
identity: identity,
|
||||
connManager: connManager,
|
||||
gater: gater,
|
||||
rcmgr: rcmgr,
|
||||
conns: make(map[quic.Connection]*conn),
|
||||
holePunching: make(map[holePunchKey]*activeHolePunch),
|
||||
rnd: *rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
|
||||
listeners: make(map[string][]*virtualListener),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Dial dials a new QUIC connection
|
||||
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (_c tpt.CapableConn, _err error) {
|
||||
if ok, isClient, _ := network.GetSimultaneousConnect(ctx); ok && !isClient {
|
||||
return t.holePunch(ctx, raddr, p)
|
||||
}
|
||||
|
||||
scope, err := t.rcmgr.OpenConnection(network.DirOutbound, false, raddr)
|
||||
if err != nil {
|
||||
log.Debugw("resource manager blocked outgoing connection", "peer", p, "addr", raddr, "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := t.dialWithScope(ctx, raddr, p, scope)
|
||||
if err != nil {
|
||||
scope.Done()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (t *transport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p peer.ID, scope network.ConnManagementScope) (tpt.CapableConn, error) {
|
||||
if err := scope.SetPeer(p); err != nil {
|
||||
log.Debugw("resource manager blocked outgoing connection for peer", "peer", p, "addr", raddr, "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConf, keyCh := t.identity.ConfigForPeer(p)
|
||||
pconn, err := t.connManager.DialQUIC(ctx, raddr, tlsConf, t.allowWindowIncrease)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Should be ready by this point, don't block.
|
||||
var remotePubKey ic.PubKey
|
||||
select {
|
||||
case remotePubKey = <-keyCh:
|
||||
default:
|
||||
}
|
||||
if remotePubKey == nil {
|
||||
pconn.CloseWithError(1, "")
|
||||
return nil, errors.New("p2p/transport/quic BUG: expected remote pub key to be set")
|
||||
}
|
||||
|
||||
localMultiaddr, err := quicreuse.ToQuicMultiaddr(pconn.LocalAddr(), pconn.ConnectionState().Version)
|
||||
if err != nil {
|
||||
pconn.CloseWithError(1, "")
|
||||
return nil, err
|
||||
}
|
||||
c := &conn{
|
||||
quicConn: pconn,
|
||||
transport: t,
|
||||
scope: scope,
|
||||
localPeer: t.localPeer,
|
||||
localMultiaddr: localMultiaddr,
|
||||
remotePubKey: remotePubKey,
|
||||
remotePeerID: p,
|
||||
remoteMultiaddr: raddr,
|
||||
}
|
||||
if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, c) {
|
||||
pconn.CloseWithError(errorCodeConnectionGating, "connection gated")
|
||||
return nil, fmt.Errorf("secured connection gated")
|
||||
}
|
||||
t.addConn(pconn, c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (t *transport) addConn(conn quic.Connection, c *conn) {
|
||||
t.connMx.Lock()
|
||||
t.conns[conn] = c
|
||||
t.connMx.Unlock()
|
||||
}
|
||||
|
||||
func (t *transport) removeConn(conn quic.Connection) {
|
||||
t.connMx.Lock()
|
||||
delete(t.conns, conn)
|
||||
t.connMx.Unlock()
|
||||
}
|
||||
|
||||
func (t *transport) holePunch(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
|
||||
network, saddr, err := manet.DialArgs(raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := net.ResolveUDPAddr(network, saddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr, err := t.connManager.TransportForDial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tr.DecreaseCount()
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, HolePunchTimeout)
|
||||
defer cancel()
|
||||
|
||||
key := holePunchKey{addr: addr.String(), peer: p}
|
||||
t.holePunchingMx.Lock()
|
||||
if _, ok := t.holePunching[key]; ok {
|
||||
t.holePunchingMx.Unlock()
|
||||
return nil, fmt.Errorf("already punching hole for %s", addr)
|
||||
}
|
||||
connCh := make(chan tpt.CapableConn, 1)
|
||||
t.holePunching[key] = &activeHolePunch{connCh: connCh}
|
||||
t.holePunchingMx.Unlock()
|
||||
|
||||
var timer *time.Timer
|
||||
defer func() {
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
payload := make([]byte, 64)
|
||||
var punchErr error
|
||||
loop:
|
||||
for i := 0; ; i++ {
|
||||
t.rndMx.Lock()
|
||||
_, err := t.rnd.Read(payload)
|
||||
t.rndMx.Unlock()
|
||||
if err != nil {
|
||||
punchErr = err
|
||||
break
|
||||
}
|
||||
if _, err := tr.WriteTo(payload, addr); err != nil {
|
||||
punchErr = err
|
||||
break
|
||||
}
|
||||
|
||||
maxSleep := 10 * (i + 1) * (i + 1) // in ms
|
||||
if maxSleep > 200 {
|
||||
maxSleep = 200
|
||||
}
|
||||
d := 10*time.Millisecond + time.Duration(rand.Intn(maxSleep))*time.Millisecond
|
||||
if timer == nil {
|
||||
timer = time.NewTimer(d)
|
||||
} else {
|
||||
timer.Reset(d)
|
||||
}
|
||||
select {
|
||||
case c := <-connCh:
|
||||
t.holePunchingMx.Lock()
|
||||
delete(t.holePunching, key)
|
||||
t.holePunchingMx.Unlock()
|
||||
return c, nil
|
||||
case <-timer.C:
|
||||
case <-ctx.Done():
|
||||
punchErr = ErrHolePunching
|
||||
break loop
|
||||
}
|
||||
}
|
||||
// we only arrive here if punchErr != nil
|
||||
t.holePunchingMx.Lock()
|
||||
defer func() {
|
||||
delete(t.holePunching, key)
|
||||
t.holePunchingMx.Unlock()
|
||||
}()
|
||||
select {
|
||||
case c := <-t.holePunching[key].connCh:
|
||||
return c, nil
|
||||
default:
|
||||
return nil, punchErr
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic-v1
|
||||
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC_V1))
|
||||
|
||||
// CanDial determines if we can dial to an address
|
||||
func (t *transport) CanDial(addr ma.Multiaddr) bool {
|
||||
return dialMatcher.Matches(addr)
|
||||
}
|
||||
|
||||
// Listen listens for new QUIC connections on the passed multiaddr.
|
||||
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
|
||||
var tlsConf tls.Config
|
||||
tlsConf.GetConfigForClient = func(_ *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
// return a tls.Config that verifies the peer's certificate chain.
|
||||
// Note that since we have no way of associating an incoming QUIC connection with
|
||||
// the peer ID calculated here, we don't actually receive the peer's public key
|
||||
// from the key chan.
|
||||
conf, _ := t.identity.ConfigForPeer("")
|
||||
return conf, nil
|
||||
}
|
||||
tlsConf.NextProtos = []string{"libp2p"}
|
||||
udpAddr, version, err := quicreuse.FromQuicMultiaddr(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.listenersMu.Lock()
|
||||
defer t.listenersMu.Unlock()
|
||||
listeners := t.listeners[udpAddr.String()]
|
||||
var underlyingListener *listener
|
||||
var acceptRunner *acceptLoopRunner
|
||||
if len(listeners) != 0 {
|
||||
// We already have an underlying listener, let's use it
|
||||
underlyingListener = listeners[0].listener
|
||||
acceptRunner = listeners[0].acceptRunnner
|
||||
// Make sure our underlying listener is listening on the specified QUIC version
|
||||
if _, ok := underlyingListener.localMultiaddrs[version]; !ok {
|
||||
return nil, fmt.Errorf("can't listen on quic version %v, underlying listener doesn't support it", version)
|
||||
}
|
||||
} else {
|
||||
ln, err := t.connManager.ListenQUIC(addr, &tlsConf, t.allowWindowIncrease)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := newListener(ln, t, t.localPeer, t.privKey, t.rcmgr)
|
||||
if err != nil {
|
||||
_ = ln.Close()
|
||||
return nil, err
|
||||
}
|
||||
underlyingListener = &l
|
||||
|
||||
acceptRunner = &acceptLoopRunner{
|
||||
acceptSem: make(chan struct{}, 1),
|
||||
muxer: make(map[quic.VersionNumber]chan acceptVal),
|
||||
}
|
||||
}
|
||||
|
||||
l := &virtualListener{
|
||||
listener: underlyingListener,
|
||||
version: version,
|
||||
udpAddr: udpAddr.String(),
|
||||
t: t,
|
||||
acceptRunnner: acceptRunner,
|
||||
acceptChan: acceptRunner.AcceptForVersion(version),
|
||||
}
|
||||
|
||||
listeners = append(listeners, l)
|
||||
t.listeners[udpAddr.String()] = listeners
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (t *transport) allowWindowIncrease(conn quic.Connection, size uint64) bool {
|
||||
// If the QUIC connection tries to increase the window before we've inserted it
|
||||
// into our connections map (which we do right after dialing / accepting it),
|
||||
// we have no way to account for that memory. This should be very rare.
|
||||
// Block this attempt. The connection can request more memory later.
|
||||
t.connMx.Lock()
|
||||
c, ok := t.conns[conn]
|
||||
t.connMx.Unlock()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return c.allowWindowIncrease(size)
|
||||
}
|
||||
|
||||
// Proxy returns true if this transport proxies.
|
||||
func (t *transport) Proxy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Protocols returns the set of protocols handled by this transport.
|
||||
func (t *transport) Protocols() []int {
|
||||
return t.connManager.Protocols()
|
||||
}
|
||||
|
||||
func (t *transport) String() string {
|
||||
return "QUIC"
|
||||
}
|
||||
|
||||
func (t *transport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *transport) CloseVirtualListener(l *virtualListener) error {
|
||||
t.listenersMu.Lock()
|
||||
defer t.listenersMu.Unlock()
|
||||
|
||||
var err error
|
||||
listeners := t.listeners[l.udpAddr]
|
||||
if len(listeners) == 1 {
|
||||
// This is the last virtual listener here, so we can close the underlying listener
|
||||
err = l.listener.Close()
|
||||
delete(t.listeners, l.udpAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < len(listeners); i++ {
|
||||
// Swap remove
|
||||
if l == listeners[i] {
|
||||
listeners[i] = listeners[len(listeners)-1]
|
||||
listeners = listeners[:len(listeners)-1]
|
||||
t.listeners[l.udpAddr] = listeners
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
175
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/virtuallistener.go
generated
vendored
Normal file
175
vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/virtuallistener.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
package libp2pquic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
tpt "github.com/libp2p/go-libp2p/core/transport"
|
||||
"github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
const acceptBufferPerVersion = 4
|
||||
|
||||
// virtualListener is a listener that exposes a single multiaddr but uses another listener under the hood
|
||||
type virtualListener struct {
|
||||
*listener
|
||||
udpAddr string
|
||||
version quic.VersionNumber
|
||||
t *transport
|
||||
acceptRunnner *acceptLoopRunner
|
||||
acceptChan chan acceptVal
|
||||
}
|
||||
|
||||
var _ tpt.Listener = &virtualListener{}
|
||||
|
||||
func (l *virtualListener) Multiaddr() ma.Multiaddr {
|
||||
return l.listener.localMultiaddrs[l.version]
|
||||
}
|
||||
|
||||
func (l *virtualListener) Close() error {
|
||||
l.acceptRunnner.RmAcceptForVersion(l.version, tpt.ErrListenerClosed)
|
||||
return l.t.CloseVirtualListener(l)
|
||||
}
|
||||
|
||||
func (l *virtualListener) Accept() (tpt.CapableConn, error) {
|
||||
return l.acceptRunnner.Accept(l.listener, l.version, l.acceptChan)
|
||||
}
|
||||
|
||||
type acceptVal struct {
|
||||
conn tpt.CapableConn
|
||||
err error
|
||||
}
|
||||
|
||||
type acceptLoopRunner struct {
|
||||
acceptSem chan struct{}
|
||||
|
||||
muxerMu sync.Mutex
|
||||
muxer map[quic.VersionNumber]chan acceptVal
|
||||
muxerClosed bool
|
||||
}
|
||||
|
||||
func (r *acceptLoopRunner) AcceptForVersion(v quic.VersionNumber) chan acceptVal {
|
||||
r.muxerMu.Lock()
|
||||
defer r.muxerMu.Unlock()
|
||||
|
||||
ch := make(chan acceptVal, acceptBufferPerVersion)
|
||||
|
||||
if _, ok := r.muxer[v]; ok {
|
||||
panic("unexpected chan already found in accept muxer")
|
||||
}
|
||||
|
||||
r.muxer[v] = ch
|
||||
return ch
|
||||
}
|
||||
|
||||
func (r *acceptLoopRunner) RmAcceptForVersion(v quic.VersionNumber, err error) {
|
||||
r.muxerMu.Lock()
|
||||
defer r.muxerMu.Unlock()
|
||||
|
||||
if r.muxerClosed {
|
||||
// Already closed, all versions are removed
|
||||
return
|
||||
}
|
||||
|
||||
ch, ok := r.muxer[v]
|
||||
if !ok {
|
||||
panic("expected chan in accept muxer")
|
||||
}
|
||||
ch <- acceptVal{err: err}
|
||||
delete(r.muxer, v)
|
||||
}
|
||||
|
||||
func (r *acceptLoopRunner) sendErrAndClose(err error) {
|
||||
r.muxerMu.Lock()
|
||||
defer r.muxerMu.Unlock()
|
||||
r.muxerClosed = true
|
||||
for k, ch := range r.muxer {
|
||||
select {
|
||||
case ch <- acceptVal{err: err}:
|
||||
default:
|
||||
}
|
||||
delete(r.muxer, k)
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// innerAccept is the inner logic of the Accept loop. Assume caller holds the
|
||||
// acceptSemaphore. May return both a nil conn and nil error if it didn't find a
|
||||
// conn with the expected version
|
||||
func (r *acceptLoopRunner) innerAccept(l *listener, expectedVersion quic.VersionNumber, bufferedConnChan chan acceptVal) (tpt.CapableConn, error) {
|
||||
select {
|
||||
// Check if we have a buffered connection first from an earlier Accept call
|
||||
case v, ok := <-bufferedConnChan:
|
||||
if !ok {
|
||||
return nil, tpt.ErrListenerClosed
|
||||
}
|
||||
return v.conn, v.err
|
||||
default:
|
||||
}
|
||||
|
||||
conn, err := l.Accept()
|
||||
|
||||
if err != nil {
|
||||
r.sendErrAndClose(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, version, err := quicreuse.FromQuicMultiaddr(conn.RemoteMultiaddr())
|
||||
if err != nil {
|
||||
r.sendErrAndClose(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if version == expectedVersion {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// This wasn't the version we were expecting, lets queue it up for a
|
||||
// future Accept call with a different version
|
||||
r.muxerMu.Lock()
|
||||
ch, ok := r.muxer[version]
|
||||
r.muxerMu.Unlock()
|
||||
|
||||
if !ok {
|
||||
// Nothing to handle this connection version. Close it
|
||||
conn.Close()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Non blocking
|
||||
select {
|
||||
case ch <- acceptVal{conn: conn}:
|
||||
default:
|
||||
// accept queue filled up, drop the connection
|
||||
conn.Close()
|
||||
log.Warn("Accept queue filled. Dropping connection.")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *acceptLoopRunner) Accept(l *listener, expectedVersion quic.VersionNumber, bufferedConnChan chan acceptVal) (tpt.CapableConn, error) {
|
||||
for {
|
||||
var conn tpt.CapableConn
|
||||
var err error
|
||||
select {
|
||||
case r.acceptSem <- struct{}{}:
|
||||
conn, err = r.innerAccept(l, expectedVersion, bufferedConnChan)
|
||||
<-r.acceptSem
|
||||
|
||||
if conn == nil && err == nil {
|
||||
// Didn't find a conn for the expected version and there was no error, lets try again
|
||||
continue
|
||||
}
|
||||
case v, ok := <-bufferedConnChan:
|
||||
if !ok {
|
||||
return nil, tpt.ErrListenerClosed
|
||||
}
|
||||
conn = v.conn
|
||||
err = v.err
|
||||
}
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user