Skip to content

Commit ed59abf

Browse files
boks1971Sean-Der
authored andcommitted
Support stand alone STUN server mode
Running in stand alone STUN server does not start due to packet conn config requiring relay address config during validation. To enable that mode, bypass validation if relay address config in packet conn config is nil. Note that it is still validated in `ListenerConfig`.
1 parent 33c87de commit ed59abf

File tree

6 files changed

+131
-3
lines changed

6 files changed

+131
-3
lines changed

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ var (
2828
errNonSTUNMessage = errors.New("non-STUN message from STUN server")
2929
errFailedToDecodeSTUN = errors.New("failed to decode STUN message")
3030
errUnexpectedSTUNRequestMessage = errors.New("unexpected STUN request message")
31+
errRelayAddressGeneratorNil = errors.New("RelayAddressGenerator is nil")
3132
)

examples/stun-only-server/main.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
// Package main implements a simple TURN server
5+
package main
6+
7+
import (
8+
"flag"
9+
"log"
10+
"net"
11+
"os"
12+
"os/signal"
13+
"strconv"
14+
"syscall"
15+
16+
"github.com/pion/turn/v3"
17+
)
18+
19+
func main() {
20+
publicIP := flag.String("public-ip", "", "IP Address that STUN can be contacted by.")
21+
port := flag.Int("port", 3478, "Listening port.")
22+
flag.Parse()
23+
24+
if len(*publicIP) == 0 {
25+
log.Fatalf("'public-ip' is required")
26+
}
27+
28+
// Create a UDP listener to pass into pion/turn
29+
// pion/turn itself doesn't allocate any UDP sockets, but lets the user pass them in
30+
// this allows us to add logging, storage or modify inbound/outbound traffic
31+
udpListener, err := net.ListenPacket("udp4", "0.0.0.0:"+strconv.Itoa(*port))
32+
if err != nil {
33+
log.Panicf("Failed to create STUN server listener: %s", err)
34+
}
35+
36+
s, err := turn.NewServer(turn.ServerConfig{
37+
// PacketConnConfigs is a list of UDP Listeners and the configuration around them
38+
PacketConnConfigs: []turn.PacketConnConfig{
39+
{
40+
PacketConn: udpListener,
41+
},
42+
},
43+
})
44+
if err != nil {
45+
log.Panic(err)
46+
}
47+
48+
// Block until user sends SIGINT or SIGTERM
49+
sigs := make(chan os.Signal, 1)
50+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
51+
<-sigs
52+
53+
if err = s.Close(); err != nil {
54+
log.Panic(err)
55+
}
56+
}

internal/server/util.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method)
6666
realmAttr := &stun.Realm{}
6767
badRequestMsg := buildMsg(m.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
6868

69+
// No Auth handler is set, server is running in STUN only mode
70+
// Respond with 400 so clients don't retry
71+
if r.AuthHandler == nil {
72+
sendErr := buildAndSend(r.Conn, r.SrcAddr, badRequestMsg...)
73+
return nil, false, sendErr
74+
}
75+
6976
if err := nonceAttr.GetFrom(m); err != nil {
7077
return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
7178
}

server.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,25 @@ func (s *Server) readListener(l net.Listener, am *allocation.Manager) {
167167
}
168168
}
169169

170+
type nilAddressGenerator struct{}
171+
172+
func (n *nilAddressGenerator) Validate() error { return errRelayAddressGeneratorNil }
173+
174+
func (n *nilAddressGenerator) AllocatePacketConn(string, int) (net.PacketConn, net.Addr, error) {
175+
return nil, nil, errRelayAddressGeneratorNil
176+
}
177+
178+
func (n *nilAddressGenerator) AllocateConn(string, int) (net.Conn, net.Addr, error) {
179+
return nil, nil, errRelayAddressGeneratorNil
180+
}
181+
170182
func (s *Server) createAllocationManager(addrGenerator RelayAddressGenerator, handler PermissionHandler) (*allocation.Manager, error) {
171183
if handler == nil {
172184
handler = DefaultPermissionHandler
173185
}
186+
if addrGenerator == nil {
187+
addrGenerator = &nilAddressGenerator{}
188+
}
174189

175190
am, err := allocation.NewManager(allocation.ManagerConfig{
176191
AllocatePacketConn: addrGenerator.AllocatePacketConn,

server_config.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,14 @@ func (c *PacketConnConfig) validate() error {
5757
if c.PacketConn == nil {
5858
return errConnUnset
5959
}
60-
if c.RelayAddressGenerator == nil {
61-
return errRelayAddressGeneratorUnset
60+
61+
if c.RelayAddressGenerator != nil {
62+
if err := c.RelayAddressGenerator.Validate(); err != nil {
63+
return err
64+
}
6265
}
6366

64-
return c.RelayAddressGenerator.Validate()
67+
return nil
6568
}
6669

6770
// ListenerConfig is a single net.Listener to accept connections on. This will be used for TCP, TLS and DTLS listeners

server_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,52 @@ func TestConsumeSingleTURNFrame(t *testing.T) {
570570
}
571571
}
572572

573+
func TestSTUNOnly(t *testing.T) {
574+
serverAddr, err := net.ResolveUDPAddr("udp4", "0.0.0.0:3478")
575+
assert.NoError(t, err)
576+
577+
serverConn, err := net.ListenPacket(serverAddr.Network(), serverAddr.String())
578+
assert.NoError(t, err)
579+
580+
defer serverConn.Close() //nolint:errcheck
581+
582+
server, err := NewServer(ServerConfig{
583+
PacketConnConfigs: []PacketConnConfig{{
584+
PacketConn: serverConn,
585+
}},
586+
Realm: "pion.ly",
587+
LoggerFactory: logging.NewDefaultLoggerFactory(),
588+
})
589+
assert.NoError(t, err)
590+
591+
defer server.Close() //nolint:errcheck
592+
593+
conn, err := net.ListenPacket("udp4", "0.0.0.0:0")
594+
assert.NoError(t, err)
595+
596+
client, err := NewClient(&ClientConfig{
597+
Conn: conn,
598+
STUNServerAddr: "127.0.0.1:3478",
599+
TURNServerAddr: "127.0.0.1:3478",
600+
Username: "user",
601+
Password: "pass",
602+
Realm: "pion.ly",
603+
LoggerFactory: logging.NewDefaultLoggerFactory(),
604+
})
605+
assert.NoError(t, err)
606+
assert.NoError(t, client.Listen())
607+
defer client.Close()
608+
609+
reflAddr, err := client.SendBindingRequest()
610+
assert.NoError(t, err)
611+
612+
_, ok := reflAddr.(*net.UDPAddr)
613+
assert.True(t, ok)
614+
615+
_, err = client.Allocate()
616+
assert.Equal(t, err.Error(), "Allocate error response (error 400: )")
617+
}
618+
573619
func RunBenchmarkServer(b *testing.B, clientNum int) {
574620
loggerFactory := logging.NewDefaultLoggerFactory()
575621
credMap := map[string][]byte{

0 commit comments

Comments
 (0)