package server
import (
"errors"
"fmt"
"net"
"time"
"github.com/pion/stun/v3"
"github.com/pion/turn/v4/internal/proto"
)
const (
maximumAllocationLifetime = time .Hour
)
func buildAndSend(conn net .PacketConn , dst net .Addr , attrs ...stun .Setter ) error {
msg , err := stun .Build (attrs ...)
if err != nil {
return err
}
_, err = conn .WriteTo (msg .Raw , dst )
if errors .Is (err , net .ErrClosed ) {
return nil
}
return err
}
func buildAndSendErr(conn net .PacketConn , dst net .Addr , err error , attrs ...stun .Setter ) error {
if sendErr := buildAndSend (conn , dst , attrs ...); sendErr != nil {
err = fmt .Errorf ("%w %v %v" , errFailedToSendError , sendErr , err )
}
return err
}
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 authenticateRequest(req Request , stunMsg *stun .Message , callingMethod stun .Method ) (
stun .MessageIntegrity ,
bool ,
error ,
) {
respondWithNonce := func (responseCode stun .ErrorCode ) (stun .MessageIntegrity , bool , error ) {
nonce , err := req .NonceHash .Generate ()
if err != nil {
return nil , false , err
}
return nil , false , buildAndSend (req .Conn , req .SrcAddr , buildMsg (stunMsg .TransactionID ,
stun .NewType (callingMethod , stun .ClassErrorResponse ),
&stun .ErrorCodeAttribute {Code : responseCode },
stun .NewNonce (nonce ),
stun .NewRealm (req .Realm ),
)...)
}
if !stunMsg .Contains (stun .AttrMessageIntegrity ) {
return respondWithNonce (stun .CodeUnauthorized )
}
nonceAttr := &stun .Nonce {}
usernameAttr := &stun .Username {}
realmAttr := &stun .Realm {}
badRequestMsg := buildMsg (
stunMsg .TransactionID ,
stun .NewType (callingMethod , stun .ClassErrorResponse ),
&stun .ErrorCodeAttribute {Code : stun .CodeBadRequest },
)
if req .AuthHandler == nil {
sendErr := buildAndSend (req .Conn , req .SrcAddr , badRequestMsg ...)
return nil , false , sendErr
}
if err := nonceAttr .GetFrom (stunMsg ); err != nil {
return nil , false , buildAndSendErr (req .Conn , req .SrcAddr , err , badRequestMsg ...)
}
if err := req .NonceHash .Validate (nonceAttr .String ()); err != nil {
return respondWithNonce (stun .CodeStaleNonce )
}
if err := realmAttr .GetFrom (stunMsg ); err != nil {
return nil , false , buildAndSendErr (req .Conn , req .SrcAddr , err , badRequestMsg ...)
} else if err := usernameAttr .GetFrom (stunMsg ); err != nil {
return nil , false , buildAndSendErr (req .Conn , req .SrcAddr , err , badRequestMsg ...)
}
ourKey , ok := req .AuthHandler (usernameAttr .String (), realmAttr .String (), req .SrcAddr )
if !ok {
return nil , false , buildAndSendErr (
req .Conn ,
req .SrcAddr ,
fmt .Errorf ("%w %s" , errNoSuchUser , usernameAttr .String ()),
badRequestMsg ...,
)
}
if err := stun .MessageIntegrity (ourKey ).Check (stunMsg ); err != nil {
return nil , false , buildAndSendErr (req .Conn , req .SrcAddr , err , badRequestMsg ...)
}
return stun .MessageIntegrity (ourKey ), true , nil
}
func allocationLifeTime(m *stun .Message ) time .Duration {
lifetimeDuration := proto .DefaultLifetime
var lifetime proto .Lifetime
if err := lifetime .GetFrom (m ); err == nil {
if lifetime .Duration < maximumAllocationLifetime {
lifetimeDuration = lifetime .Duration
}
}
return lifetimeDuration
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .