Skip to content
This repository has been archived by the owner on May 26, 2022. It is now read-only.

Commit

Permalink
support hole punching
Browse files Browse the repository at this point in the history
  • Loading branch information
vyzo committed Feb 19, 2021
1 parent f0e6178 commit 5bc579f
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
5 changes: 5 additions & 0 deletions listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ func (l *listener) setupConn(sess quic.Session) (*conn, error) {
// Close closes the listener.
func (l *listener) Close() error {
defer l.conn.DecreaseCount()
defer func() {
l.transport.listeningMx.Lock()
delete(l.transport.listening, l.localMultiaddr.String())
l.transport.listeningMx.Unlock()
}()
return l.quicListener.Close()
}

Expand Down
56 changes: 56 additions & 0 deletions transport.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package libp2pquic

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"math/rand"
"net"
"sync"
"time"

"github.com/libp2p/go-libp2p-core/connmgr"
n "github.com/libp2p/go-libp2p-core/network"
Expand All @@ -15,6 +19,7 @@ import (

logging "github.com/ipfs/go-log"
ic "github.com/libp2p/go-libp2p-core/crypto"
in "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"
Expand Down Expand Up @@ -96,6 +101,8 @@ type transport struct {
serverConfig *quic.Config
clientConfig *quic.Config
gater connmgr.ConnectionGater
listeningMx sync.Mutex
listening map[string]struct{}
}

var _ tpt.Transport = &transport{}
Expand Down Expand Up @@ -138,6 +145,7 @@ func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (
serverConfig: config,
clientConfig: config.Clone(),
gater: gater,
listening: make(map[string]struct{}),
}, nil
}

Expand All @@ -151,15 +159,28 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp
if err != nil {
return nil, err
}

remoteMultiaddr, err := toQuicMultiaddr(addr)
if err != nil {
return nil, err
}
tlsConf, keyCh := t.identity.ConfigForPeer(p)

if simConnect, _ := in.GetSimultaneousConnect(ctx); simConnect {
if bytes.Compare([]byte(t.localPeer), []byte(p)) < 0 {
err = t.holePunch(network, addr)
if err == nil {
err = errors.New("hole punching attempted; no active dial")
}
return nil, err
}
}

pconn, err := t.connManager.Dial(network, addr)
if err != nil {
return nil, err
}

sess, err := quicDialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
if err != nil {
pconn.DecreaseCount()
Expand Down Expand Up @@ -202,6 +223,37 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp
return conn, nil
}

func (t *transport) holePunch(network string, addr *net.UDPAddr) error {
pconn, err := t.connManager.Dial(network, addr)
if err != nil {
return err
}
defer pconn.DecreaseCount()

conn := pconn.UDPConn

payload := make([]byte, 64)
_, err = rand.Read(payload)
if err != nil {
return err
}

_, err = conn.WriteToUDP(payload, addr)
if err != nil {
return err
}

for i := 0; i < 10; i++ {
time.Sleep(time.Duration(rand.Intn(i * int(10*time.Millisecond))))
_, err := conn.WriteToUDP(payload, addr)
if err != nil {
return err
}
}

return nil
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

Expand Down Expand Up @@ -229,6 +281,10 @@ func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
conn.DecreaseCount()
return nil, err
}

t.listeningMx.Lock()
t.listening[addr.String()] = struct{}{}
t.listeningMx.Unlock()
return ln, nil
}

Expand Down

0 comments on commit 5bc579f

Please sign in to comment.