mirror of
https://github.com/hanzokms/turn.git
synced 2026-10-30 20:50:36 +00:00
270 lines
6.6 KiB
Go
270 lines
6.6 KiB
Go
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//go:build !js
|
|
// +build !js
|
|
|
|
package turn
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pion/logging"
|
|
"github.com/pion/stun/v3"
|
|
"github.com/hanzokms/turn/v4/internal/proto"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func buildMsg(
|
|
transactionID [stun.TransactionIDSize]byte,
|
|
msgType stun.MessageType,
|
|
additional ...stun.Setter,
|
|
) []stun.Setter {
|
|
return append([]stun.Setter{&stun.Message{TransactionID: transactionID}, msgType}, additional...)
|
|
}
|
|
|
|
func createListeningTestClient(t *testing.T, loggerFactory logging.LoggerFactory) (*Client, net.PacketConn, bool) {
|
|
t.Helper()
|
|
|
|
conn, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
assert.NoError(t, err)
|
|
|
|
c, err := NewClient(&ClientConfig{
|
|
Conn: conn,
|
|
Software: "TEST SOFTWARE",
|
|
LoggerFactory: loggerFactory,
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, c.Listen())
|
|
|
|
return c, conn, true
|
|
}
|
|
|
|
func createListeningTestClientWithSTUNServ(t *testing.T, loggerFactory logging.LoggerFactory) ( // nolint:lll
|
|
*Client, net.PacketConn,
|
|
bool,
|
|
) {
|
|
t.Helper()
|
|
|
|
conn, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
assert.NoError(t, err)
|
|
|
|
addr := "stun1.l.google.com:19302"
|
|
|
|
c, err := NewClient(&ClientConfig{
|
|
STUNServerAddr: addr,
|
|
Conn: conn,
|
|
LoggerFactory: loggerFactory,
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, c.Listen())
|
|
|
|
return c, conn, true
|
|
}
|
|
|
|
func TestClientWithSTUN(t *testing.T) {
|
|
loggerFactory := logging.NewDefaultLoggerFactory()
|
|
log := loggerFactory.NewLogger("test")
|
|
|
|
t.Run("SendBindingRequest", func(t *testing.T) {
|
|
client, pc, ok := createListeningTestClientWithSTUNServ(t, loggerFactory)
|
|
if !ok {
|
|
return
|
|
}
|
|
defer client.Close()
|
|
|
|
resp, err := client.SendBindingRequest()
|
|
assert.NoError(t, err, "should succeed")
|
|
log.Debugf("mapped-addr: %s", resp)
|
|
assert.Equal(t, 0, client.trMap.Size(), "should be no transaction left")
|
|
assert.NoError(t, pc.Close())
|
|
})
|
|
|
|
t.Run("SendBindingRequestTo Parallel", func(t *testing.T) {
|
|
client, pc, ok := createListeningTestClient(t, loggerFactory)
|
|
if !ok {
|
|
return
|
|
}
|
|
defer client.Close()
|
|
|
|
// Simple channel fo go routine start signaling
|
|
started := make(chan struct{})
|
|
finished := make(chan struct{})
|
|
var err1 error
|
|
|
|
to, err := net.ResolveUDPAddr("udp4", "stun1.l.google.com:19302")
|
|
assert.NoError(t, err)
|
|
|
|
// stun1.l.google.com:19302, more at https://gist.github.com/zziuni/3741933#file-stuns-L5
|
|
go func() {
|
|
close(started)
|
|
_, err1 = client.SendBindingRequestTo(to)
|
|
close(finished)
|
|
}()
|
|
|
|
// Block until go routine is started to make two almost parallel requests
|
|
<-started
|
|
|
|
if _, err = client.SendBindingRequestTo(to); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
<-finished
|
|
if err1 != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
assert.NoError(t, pc.Close())
|
|
})
|
|
|
|
t.Run("NewClient should fail if Conn is nil", func(t *testing.T) {
|
|
_, err := NewClient(&ClientConfig{
|
|
LoggerFactory: loggerFactory,
|
|
})
|
|
assert.Error(t, err, "should fail")
|
|
})
|
|
|
|
t.Run("SendBindingRequestTo timeout", func(t *testing.T) {
|
|
c, pc, ok := createListeningTestClient(t, loggerFactory)
|
|
if !ok {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
|
|
to, err := net.ResolveUDPAddr("udp4", "127.0.0.1:9")
|
|
assert.NoError(t, err)
|
|
|
|
c.rto = 10 * time.Millisecond // Force short timeout
|
|
|
|
_, err = c.SendBindingRequestTo(to)
|
|
assert.NotNil(t, err)
|
|
assert.NoError(t, pc.Close())
|
|
})
|
|
}
|
|
|
|
// Create an allocation, and then delete all nonces
|
|
// The subsequent Write on the allocation will cause a CreatePermission
|
|
// which will be forced to handle a stale nonce response.
|
|
func TestClientNonceExpiration(t *testing.T) {
|
|
udpListener, err := net.ListenPacket("udp4", "0.0.0.0:3478")
|
|
assert.NoError(t, err)
|
|
|
|
server, err := NewServer(ServerConfig{
|
|
AuthHandler: func(username, realm string, _ net.Addr) (key []byte, ok bool) {
|
|
return GenerateAuthKey(username, realm, "pass"), true
|
|
},
|
|
PacketConnConfigs: []PacketConnConfig{
|
|
{
|
|
PacketConn: udpListener,
|
|
RelayAddressGenerator: &RelayAddressGeneratorStatic{
|
|
RelayAddress: net.ParseIP("127.0.0.1"),
|
|
Address: "0.0.0.0",
|
|
},
|
|
},
|
|
},
|
|
Realm: "pion.ly",
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
conn, err := net.ListenPacket("udp4", "0.0.0.0:0")
|
|
assert.NoError(t, err)
|
|
|
|
// nolint: goconst
|
|
addr := "127.0.0.1:3478"
|
|
|
|
client, err := NewClient(&ClientConfig{
|
|
Conn: conn,
|
|
STUNServerAddr: addr,
|
|
TURNServerAddr: addr,
|
|
Username: "foo",
|
|
Password: "pass",
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, client.Listen())
|
|
|
|
allocation, err := client.Allocate()
|
|
assert.NoError(t, err)
|
|
|
|
_, err = allocation.WriteTo([]byte{0x00}, &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8080})
|
|
assert.NoError(t, err)
|
|
|
|
// Shutdown
|
|
assert.NoError(t, allocation.Close())
|
|
assert.NoError(t, conn.Close())
|
|
assert.NoError(t, server.Close())
|
|
}
|
|
|
|
// Create a TCP-based allocation and verify allocation can be created.
|
|
func TestTCPClient(t *testing.T) {
|
|
// Setup server
|
|
tcpListener, err := net.Listen("tcp4", "0.0.0.0:13478") //nolint: gosec
|
|
require.NoError(t, err)
|
|
|
|
server, err := NewServer(ServerConfig{
|
|
AuthHandler: func(username, realm string, _ net.Addr) (key []byte, ok bool) {
|
|
return GenerateAuthKey(username, realm, "pass"), true
|
|
},
|
|
ListenerConfigs: []ListenerConfig{
|
|
{
|
|
Listener: tcpListener,
|
|
RelayAddressGenerator: &RelayAddressGeneratorStatic{
|
|
RelayAddress: net.ParseIP("127.0.0.1"),
|
|
Address: "0.0.0.0",
|
|
},
|
|
},
|
|
},
|
|
Realm: "pion.ly",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Setup clients
|
|
conn, err := net.Dial("tcp", "127.0.0.1:13478")
|
|
require.NoError(t, err)
|
|
|
|
serverAddr := "127.0.0.1:13478"
|
|
require.NoError(t, err)
|
|
|
|
client, err := NewClient(&ClientConfig{
|
|
Conn: NewSTUNConn(conn),
|
|
STUNServerAddr: serverAddr,
|
|
TURNServerAddr: serverAddr,
|
|
Username: "foo",
|
|
Password: "pass",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NoError(t, client.Listen())
|
|
|
|
require.Equal(t, serverAddr, client.STUNServerAddr().String())
|
|
|
|
allocation, err := client.AllocateTCP()
|
|
require.NoError(t, err)
|
|
|
|
peerAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12345")
|
|
require.NoError(t, err)
|
|
err = client.CreatePermission(peerAddr)
|
|
require.NoError(t, err)
|
|
|
|
var cid proto.ConnectionID = 5
|
|
transactionID := [stun.TransactionIDSize]byte{1, 2, 3}
|
|
attrs := buildMsg(
|
|
transactionID,
|
|
stun.NewType(stun.MethodConnectionAttempt, stun.ClassIndication),
|
|
cid,
|
|
proto.PeerAddress{IP: peerAddr.IP, Port: peerAddr.Port},
|
|
)
|
|
|
|
msg, err := stun.Build(attrs...)
|
|
require.NoError(t, err)
|
|
|
|
err = client.handleSTUNMessage(msg.Raw, peerAddr)
|
|
require.NoError(t, err)
|
|
|
|
// Shutdown
|
|
require.NoError(t, allocation.Close())
|
|
require.NoError(t, conn.Close())
|
|
require.NoError(t, server.Close())
|
|
}
|