Skip to content

Commit 100044d

Browse files
committed
Implement client side for TCP allocations
1 parent 7abfa3b commit 100044d

File tree

14 files changed

+762
-230
lines changed

14 files changed

+762
-230
lines changed

client.go

+178-61
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type ClientConfig struct {
4141
Password string
4242
Realm string
4343
Software string
44+
Protocol string
4445
RTO time.Duration
4546
Conn net.PacketConn // Listening socket (net.PacketConn)
4647
LoggerFactory logging.LoggerFactory
@@ -49,25 +50,27 @@ type ClientConfig struct {
4950

5051
// Client is a STUN server client
5152
type Client struct {
52-
conn net.PacketConn // read-only
53-
stunServ net.Addr // read-only
54-
turnServ net.Addr // read-only
55-
stunServStr string // read-only, used for de-multiplexing
56-
turnServStr string // read-only, used for de-multiplexing
57-
username stun.Username // read-only
58-
password string // read-only
59-
realm stun.Realm // read-only
60-
integrity stun.MessageIntegrity // read-only
61-
software stun.Software // read-only
62-
trMap *client.TransactionMap // thread-safe
63-
rto time.Duration // read-only
64-
relayedConn *client.UDPConn // protected by mutex ***
65-
allocTryLock client.TryLock // thread-safe
66-
listenTryLock client.TryLock // thread-safe
67-
net transport.Net // read-only
68-
mutex sync.RWMutex // thread-safe
69-
mutexTrMap sync.Mutex // thread-safe
70-
log logging.LeveledLogger // read-only
53+
conn net.PacketConn // read-only // connection to the regular turn server.
54+
stunServ net.Addr // read-only
55+
turnServ net.Addr // read-only
56+
stunServStr string // read-only, used for de-multiplexing
57+
turnServStr string // read-only, used for de-multiplexing
58+
username stun.Username // read-only
59+
password string // read-only
60+
realm stun.Realm // read-only
61+
integrity stun.MessageIntegrity // read-only
62+
software stun.Software // read-only
63+
trMap *client.TransactionMap // thread-safe
64+
rto time.Duration // read-only
65+
relayedConn *client.UDPConn // protected by mutex ***
66+
relayedTCPConn *client.TCPConn // protected by mutex ***
67+
allocTryLock client.TryLock // thread-safe
68+
listenTryLock client.TryLock // thread-safe
69+
net transport.Net // read-only
70+
mutex sync.RWMutex // thread-safe
71+
mutexTrMap sync.Mutex // thread-safe
72+
log logging.LeveledLogger // read-only
73+
protocol proto.Protocol // read-only
7174
}
7275

7376
// NewClient returns a new Client instance. listeningAddress is the address and port to listen on, default "0.0.0.0:0"
@@ -93,24 +96,47 @@ func NewClient(config *ClientConfig) (*Client, error) {
9396
log.Warn("Virtual network is enabled")
9497
}
9598

99+
protocol := proto.ProtoUDP
100+
if config.Protocol == "tcp" {
101+
protocol = proto.ProtoTCP
102+
}
103+
96104
var stunServ, turnServ net.Addr
97105
var stunServStr, turnServStr string
98106
if len(config.STUNServerAddr) > 0 {
99107
log.Debugf("resolving %s", config.STUNServerAddr)
100-
stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr)
101-
if err != nil {
102-
return nil, err
108+
switch protocol {
109+
case proto.ProtoUDP:
110+
stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr)
111+
if err != nil {
112+
return nil, err
113+
}
114+
stunServStr = stunServ.String()
115+
case proto.ProtoTCP:
116+
stunServ, err = config.Net.ResolveTCPAddr("tcp4", config.STUNServerAddr)
117+
if err != nil {
118+
return nil, err
119+
}
120+
stunServStr = stunServ.String()
103121
}
104-
stunServStr = stunServ.String()
105122
log.Debugf("stunServ: %s", stunServStr)
106123
}
107124
if len(config.TURNServerAddr) > 0 {
108125
log.Debugf("resolving %s", config.TURNServerAddr)
109-
turnServ, err = config.Net.ResolveUDPAddr("udp4", config.TURNServerAddr)
110-
if err != nil {
111-
return nil, err
126+
switch protocol {
127+
case proto.ProtoUDP:
128+
turnServ, err = config.Net.ResolveUDPAddr("udp4", config.TURNServerAddr)
129+
if err != nil {
130+
return nil, err
131+
}
132+
turnServStr = turnServ.String()
133+
case proto.ProtoTCP:
134+
turnServ, err = config.Net.ResolveTCPAddr("tcp4", config.TURNServerAddr)
135+
if err != nil {
136+
return nil, err
137+
}
138+
turnServStr = turnServ.String()
112139
}
113-
turnServStr = turnServ.String()
114140
log.Debugf("turnServ: %s", turnServStr)
115141
}
116142

@@ -133,6 +159,7 @@ func NewClient(config *ClientConfig) (*Client, error) {
133159
trMap: client.NewTransactionMap(),
134160
rto: rto,
135161
log: log,
162+
protocol: protocol,
136163
}
137164

138165
return c, nil
@@ -238,42 +265,34 @@ func (c *Client) SendBindingRequest() (net.Addr, error) {
238265
return c.SendBindingRequestTo(c.stunServ)
239266
}
240267

241-
// Allocate sends a TURN allocation request to the given transport address
242-
func (c *Client) Allocate() (net.PacketConn, error) {
243-
if err := c.allocTryLock.Lock(); err != nil {
244-
return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error())
245-
}
246-
defer c.allocTryLock.Unlock()
247-
248-
relayedConn := c.relayedUDPConn()
249-
if relayedConn != nil {
250-
return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String())
251-
}
268+
func (c *Client) sendAllocateRequest() (proto.RelayedAddress, proto.Lifetime, stun.Nonce, error) {
269+
var relayed proto.RelayedAddress
270+
var lifetime proto.Lifetime
271+
var nonce stun.Nonce
252272

253273
msg, err := stun.Build(
254274
stun.TransactionID,
255275
stun.NewType(stun.MethodAllocate, stun.ClassRequest),
256-
proto.RequestedTransport{Protocol: proto.ProtoUDP},
276+
proto.RequestedTransport{Protocol: c.protocol},
257277
stun.Fingerprint,
258278
)
259279
if err != nil {
260-
return nil, err
280+
return relayed, lifetime, nonce, err
261281
}
262282

263283
trRes, err := c.PerformTransaction(msg, c.turnServ, false)
264284
if err != nil {
265-
return nil, err
285+
return relayed, lifetime, nonce, err
266286
}
267287

268288
res := trRes.Msg
269289

270290
// Anonymous allocate failed, trying to authenticate.
271-
var nonce stun.Nonce
272291
if err = nonce.GetFrom(res); err != nil {
273-
return nil, err
292+
return relayed, lifetime, nonce, err
274293
}
275294
if err = c.realm.GetFrom(res); err != nil {
276-
return nil, err
295+
return relayed, lifetime, nonce, err
277296
}
278297
c.realm = append([]byte(nil), c.realm...)
279298
c.integrity = stun.NewLongTermIntegrity(
@@ -283,48 +302,101 @@ func (c *Client) Allocate() (net.PacketConn, error) {
283302
msg, err = stun.Build(
284303
stun.TransactionID,
285304
stun.NewType(stun.MethodAllocate, stun.ClassRequest),
286-
proto.RequestedTransport{Protocol: proto.ProtoUDP},
305+
proto.RequestedTransport{Protocol: c.protocol},
287306
&c.username,
288307
&c.realm,
289308
&nonce,
290309
&c.integrity,
291310
stun.Fingerprint,
292311
)
293312
if err != nil {
294-
return nil, err
313+
return relayed, lifetime, nonce, err
295314
}
296315

297316
trRes, err = c.PerformTransaction(msg, c.turnServ, false)
298317
if err != nil {
299-
return nil, err
318+
return relayed, lifetime, nonce, err
300319
}
301320
res = trRes.Msg
302321

303322
if res.Type.Class == stun.ClassErrorResponse {
304323
var code stun.ErrorCodeAttribute
305324
if err = code.GetFrom(res); err == nil {
306-
return nil, fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
325+
return relayed, lifetime, nonce, fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
307326
}
308-
return nil, fmt.Errorf("%s", res.Type) //nolint:goerr113
327+
return relayed, lifetime, nonce, fmt.Errorf("%s", res.Type) //nolint:goerr113
309328
}
310329

311330
// Getting relayed addresses from response.
312-
var relayed proto.RelayedAddress
313331
if err := relayed.GetFrom(res); err != nil {
332+
return relayed, lifetime, nonce, err
333+
}
334+
335+
// Getting lifetime from response
336+
if err := lifetime.GetFrom(res); err != nil {
337+
return relayed, lifetime, nonce, err
338+
}
339+
return relayed, lifetime, nonce, nil
340+
}
341+
342+
// Allocate sends a TURN allocation request to the given transport address
343+
func (c *Client) Allocate() (net.PacketConn, error) {
344+
if err := c.allocTryLock.Lock(); err != nil {
345+
return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error())
346+
}
347+
defer c.allocTryLock.Unlock()
348+
349+
relayedConn := c.getRelayedUDPConn()
350+
if relayedConn != nil {
351+
return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String())
352+
}
353+
354+
relayed, lifetime, nonce, err := c.sendAllocateRequest()
355+
if err != nil {
314356
return nil, err
315357
}
358+
316359
relayedAddr := &net.UDPAddr{
317360
IP: relayed.IP,
318361
Port: relayed.Port,
319362
}
320363

321-
// Getting lifetime from response
322-
var lifetime proto.Lifetime
323-
if err := lifetime.GetFrom(res); err != nil {
364+
relayedConn = client.NewUDPConn(&client.ConnConfig{
365+
Observer: c,
366+
RelayedAddr: relayedAddr,
367+
Integrity: c.integrity,
368+
Nonce: nonce,
369+
Lifetime: lifetime.Duration,
370+
Log: c.log,
371+
})
372+
c.setRelayedUDPConn(relayedConn)
373+
374+
return relayedConn, nil
375+
}
376+
377+
// Allocate TCP
378+
func (c *Client) AllocateTCP() (*client.TCPConn, error) {
379+
if err := c.allocTryLock.Lock(); err != nil {
380+
return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error())
381+
}
382+
defer c.allocTryLock.Unlock()
383+
384+
relayedConn := c.getRelayedTCPConn()
385+
if relayedConn != nil {
386+
return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String())
387+
}
388+
389+
relayed, lifetime, nonce, err := c.sendAllocateRequest()
390+
if err != nil {
324391
return nil, err
325392
}
326393

327-
relayedConn = client.NewUDPConn(&client.UDPConnConfig{
394+
relayedAddr := &net.TCPAddr{
395+
IP: relayed.IP,
396+
Port: relayed.Port,
397+
}
398+
399+
relayedConn = client.NewTCPConn(&client.ConnConfig{
328400
Observer: c,
329401
RelayedAddr: relayedAddr,
330402
Integrity: c.integrity,
@@ -333,15 +405,21 @@ func (c *Client) Allocate() (net.PacketConn, error) {
333405
Log: c.log,
334406
})
335407

336-
c.setRelayedUDPConn(relayedConn)
408+
c.setRelayedTCPConn(relayedConn)
337409

338410
return relayedConn, nil
339411
}
340412

341413
// CreatePermission Issues a CreatePermission request for the supplied addresses
342414
// as described in https://datatracker.ietf.org/doc/html/rfc5766#section-9
343415
func (c *Client) CreatePermission(addrs ...net.Addr) error {
344-
return c.relayedUDPConn().CreatePermissions(addrs...)
416+
switch c.protocol {
417+
case proto.ProtoUDP:
418+
return c.getRelayedUDPConn().CreatePermissions(addrs...)
419+
case proto.ProtoTCP:
420+
return c.getRelayedTCPConn().CreatePermissions(addrs...)
421+
}
422+
return nil
345423
}
346424

347425
// PerformTransaction performs STUN transaction
@@ -445,7 +523,8 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error {
445523
}
446524

447525
if msg.Type.Class == stun.ClassIndication {
448-
if msg.Type.Method == stun.MethodData {
526+
switch msg.Type.Method {
527+
case stun.MethodData:
449528
var peerAddr proto.PeerAddress
450529
if err := peerAddr.GetFrom(msg); err != nil {
451530
return err
@@ -462,13 +541,37 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error {
462541

463542
c.log.Debugf("data indication received from %s", from.String())
464543

465-
relayedConn := c.relayedUDPConn()
544+
relayedConn := c.getRelayedUDPConn()
466545
if relayedConn == nil {
467546
c.log.Debug("no relayed conn allocated")
468547
return nil // silently discard
469548
}
470-
471549
relayedConn.HandleInbound(data, from)
550+
case stun.MethodConnectionAttempt:
551+
var peerAddr proto.PeerAddress
552+
if err := peerAddr.GetFrom(msg); err != nil {
553+
return err
554+
}
555+
556+
from = &net.TCPAddr{
557+
IP: peerAddr.IP,
558+
Port: peerAddr.Port,
559+
}
560+
561+
var cid proto.ConnectionID
562+
if err := cid.GetFrom(msg); err != nil {
563+
return err
564+
}
565+
566+
c.log.Debugf("connection attempt from %s", from.String())
567+
568+
relayedConn := c.getRelayedTCPConn()
569+
if relayedConn == nil {
570+
c.log.Debug("no relayed conn allocated")
571+
return nil // silently discard
572+
}
573+
574+
relayedConn.HandleConnectionAttempt(data, from, cid)
472575
}
473576
return nil
474577
}
@@ -514,7 +617,7 @@ func (c *Client) handleChannelData(data []byte) error {
514617
return err
515618
}
516619

517-
relayedConn := c.relayedUDPConn()
620+
relayedConn := c.getRelayedUDPConn()
518621
if relayedConn == nil {
519622
c.log.Debug("no relayed conn allocated")
520623
return nil // silently discard
@@ -573,9 +676,23 @@ func (c *Client) setRelayedUDPConn(conn *client.UDPConn) {
573676
c.relayedConn = conn
574677
}
575678

576-
func (c *Client) relayedUDPConn() *client.UDPConn {
679+
func (c *Client) getRelayedUDPConn() *client.UDPConn {
577680
c.mutex.RLock()
578681
defer c.mutex.RUnlock()
579682

580683
return c.relayedConn
581684
}
685+
686+
func (c *Client) setRelayedTCPConn(conn *client.TCPConn) {
687+
c.mutex.Lock()
688+
defer c.mutex.Unlock()
689+
690+
c.relayedTCPConn = conn
691+
}
692+
693+
func (c *Client) getRelayedTCPConn() *client.TCPConn {
694+
c.mutex.RLock()
695+
defer c.mutex.RUnlock()
696+
697+
return c.relayedTCPConn
698+
}

0 commit comments

Comments
 (0)