package turn
import (
"encoding/binary"
"errors"
"net"
"time"
"github.com/pion/stun/v3"
"github.com/pion/turn/v4/internal/proto"
)
var (
errInvalidTURNFrame = errors .New ("data is not a valid TURN frame, no STUN or ChannelData found" )
errIncompleteTURNFrame = errors .New ("data contains incomplete STUN or TURN frame" )
)
type STUNConn struct {
nextConn net .Conn
buff []byte
}
const (
stunHeaderSize = 20
channelDataLengthSize = 2
channelDataNumberSize = channelDataLengthSize
channelDataHeaderSize = channelDataLengthSize + channelDataNumberSize
channelDataPadding = 4
)
func consumeSingleTURNFrame(b []byte ) (int , error ) {
if len (b ) < 9 {
return 0 , errIncompleteTURNFrame
}
var datagramSize uint16
switch {
case stun .IsMessage (b ):
datagramSize = binary .BigEndian .Uint16 (b [2 :4 ]) + stunHeaderSize
case proto .ChannelNumber (binary .BigEndian .Uint16 (b [0 :2 ])).Valid ():
datagramSize = binary .BigEndian .Uint16 (b [channelDataNumberSize :channelDataHeaderSize ])
if paddingOverflow := (datagramSize + channelDataPadding ) % channelDataPadding ; paddingOverflow != 0 {
datagramSize = (datagramSize + channelDataPadding ) - paddingOverflow
}
datagramSize += channelDataHeaderSize
case len (b ) < stunHeaderSize :
return 0 , errIncompleteTURNFrame
default :
return 0 , errInvalidTURNFrame
}
if len (b ) < int (datagramSize ) {
return 0 , errIncompleteTURNFrame
}
return int (datagramSize ), nil
}
func (s *STUNConn ) ReadFrom (payload []byte ) (n int , addr net .Addr , err error ) {
n , err = consumeSingleTURNFrame (s .buff )
if errors .Is (err , errInvalidTURNFrame ) {
return 0 , nil , err
} else if err == nil {
copy (payload , s .buff [:n ])
s .buff = s .buff [n :]
return n , s .nextConn .RemoteAddr (), nil
}
n , err = s .nextConn .Read (payload )
if err != nil {
return 0 , nil , err
}
s .buff = append (s .buff , append ([]byte {}, payload [:n ]...)...)
return s .ReadFrom (payload )
}
func (s *STUNConn ) WriteTo (payload []byte , _ net .Addr ) (n int , err error ) {
return s .nextConn .Write (payload )
}
func (s *STUNConn ) Close () error {
return s .nextConn .Close ()
}
func (s *STUNConn ) LocalAddr () net .Addr {
return s .nextConn .LocalAddr ()
}
func (s *STUNConn ) SetDeadline (t time .Time ) error {
return s .nextConn .SetDeadline (t )
}
func (s *STUNConn ) SetReadDeadline (t time .Time ) error {
return s .nextConn .SetReadDeadline (t )
}
func (s *STUNConn ) SetWriteDeadline (t time .Time ) error {
return s .nextConn .SetWriteDeadline (t )
}
func NewSTUNConn (nextConn net .Conn ) *STUNConn {
return &STUNConn {nextConn : nextConn }
}
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 .