package sdp
import (
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"sync"
)
var (
errSDPInvalidSyntax = errors .New ("sdp: invalid syntax" )
errSDPInvalidNumericValue = errors .New ("sdp: invalid numeric value" )
errSDPInvalidValue = errors .New ("sdp: invalid value" )
errSDPInvalidPortValue = errors .New ("sdp: invalid port value" )
errSDPCacheInvalid = errors .New ("sdp: invalid cache" )
unmarshalCachePool = sync .Pool {
New : func () interface {} {
return &unmarshalCache {}
},
}
)
func (s *SessionDescription ) UnmarshalString (value string ) error {
var ok bool
lex := new (lexer )
if lex .cache , ok = unmarshalCachePool .Get ().(*unmarshalCache ); !ok {
return errSDPCacheInvalid
}
defer unmarshalCachePool .Put (lex .cache )
lex .cache .reset ()
lex .desc = s
lex .value = value
for state := s1 ; state != nil ; {
var err error
state , err = state (lex )
if err != nil {
return err
}
}
s .Attributes = lex .cache .cloneSessionAttributes ()
populateMediaAttributes (lex .cache , lex .desc )
return nil
}
func (s *SessionDescription ) Unmarshal (value []byte ) error {
return s .UnmarshalString (string (value ))
}
func s1(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
if key == 'v' {
return unmarshalProtocolVersion
}
return nil
})
}
func s2(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
if key == 'o' {
return unmarshalOrigin
}
return nil
})
}
func s3(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
if key == 's' {
return unmarshalSessionName
}
return nil
})
}
func s4(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'i' :
return unmarshalSessionInformation
case 'u' :
return unmarshalURI
case 'e' :
return unmarshalEmail
case 'p' :
return unmarshalPhone
case 'c' :
return unmarshalSessionConnectionInformation
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s5(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s6(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'p' :
return unmarshalPhone
case 'c' :
return unmarshalSessionConnectionInformation
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s7(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'u' :
return unmarshalURI
case 'e' :
return unmarshalEmail
case 'p' :
return unmarshalPhone
case 'c' :
return unmarshalSessionConnectionInformation
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s8(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'c' :
return unmarshalSessionConnectionInformation
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s9(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'z' :
return unmarshalTimeZones
case 'k' :
return unmarshalSessionEncryptionKey
case 'a' :
return unmarshalSessionAttribute
case 'r' :
return unmarshalRepeatTimes
case 't' :
return unmarshalTiming
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s10(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'e' :
return unmarshalEmail
case 'p' :
return unmarshalPhone
case 'c' :
return unmarshalSessionConnectionInformation
case 'b' :
return unmarshalSessionBandwidth
case 't' :
return unmarshalTiming
}
return nil
})
}
func s11(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalSessionAttribute
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s12(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalMediaAttribute
case 'k' :
return unmarshalMediaEncryptionKey
case 'b' :
return unmarshalMediaBandwidth
case 'c' :
return unmarshalMediaConnectionInformation
case 'i' :
return unmarshalMediaTitle
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s13(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalSessionAttribute
case 'k' :
return unmarshalSessionEncryptionKey
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s14(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalMediaAttribute
case 'k' :
return unmarshalMediaEncryptionKey
case 'b' :
return unmarshalMediaBandwidth
case 'c' :
return unmarshalMediaConnectionInformation
case 'i' :
return unmarshalMediaTitle
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s15(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalMediaAttribute
case 'k' :
return unmarshalMediaEncryptionKey
case 'b' :
return unmarshalMediaBandwidth
case 'c' :
return unmarshalMediaConnectionInformation
case 'i' :
return unmarshalMediaTitle
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func s16(l *lexer ) (stateFn , error ) {
return l .handleType (func (key byte ) stateFn {
switch key {
case 'a' :
return unmarshalMediaAttribute
case 'k' :
return unmarshalMediaEncryptionKey
case 'c' :
return unmarshalMediaConnectionInformation
case 'b' :
return unmarshalMediaBandwidth
case 'i' :
return unmarshalMediaTitle
case 'm' :
return unmarshalMediaDescription
}
return nil
})
}
func unmarshalProtocolVersion(l *lexer ) (stateFn , error ) {
version , err := l .readUint64Field ()
if err != nil {
return nil , err
}
if version != 0 {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , version )
}
if err := l .nextLine (); err != nil {
return nil , err
}
return s2 , nil
}
func unmarshalOrigin(lex *lexer ) (stateFn , error ) {
var err error
lex .desc .Origin .Username , err = lex .readField ()
if err != nil {
return nil , err
}
lex .desc .Origin .SessionID , err = lex .readUint64Field ()
if err != nil {
return nil , err
}
lex .desc .Origin .SessionVersion , err = lex .readUint64Field ()
if err != nil {
return nil , err
}
lex .desc .Origin .NetworkType , err = lex .readField ()
if err != nil {
return nil , err
}
if !anyOf (lex .desc .Origin .NetworkType , "IN" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , lex .desc .Origin .NetworkType )
}
lex .desc .Origin .AddressType , err = lex .readField ()
if err != nil {
return nil , err
}
if !anyOf (lex .desc .Origin .AddressType , "IP4" , "IP6" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , lex .desc .Origin .AddressType )
}
lex .desc .Origin .UnicastAddress , err = lex .readField ()
if err != nil {
return nil , err
}
if err := lex .nextLine (); err != nil {
return nil , err
}
return s3 , nil
}
func unmarshalSessionName(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
l .desc .SessionName = SessionName (value )
return s4 , nil
}
func unmarshalSessionInformation(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
sessionInformation := Information (value )
l .desc .SessionInformation = &sessionInformation
return s7 , nil
}
func unmarshalURI(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
l .desc .URI , err = url .Parse (value )
if err != nil {
return nil , err
}
return s10 , nil
}
func unmarshalEmail(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
emailAddress := EmailAddress (value )
l .desc .EmailAddress = &emailAddress
return s6 , nil
}
func unmarshalPhone(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
phoneNumber := PhoneNumber (value )
l .desc .PhoneNumber = &phoneNumber
return s8 , nil
}
func unmarshalSessionConnectionInformation(l *lexer ) (stateFn , error ) {
var err error
l .desc .ConnectionInformation , err = l .unmarshalConnectionInformation ()
if err != nil {
return nil , err
}
return s5 , nil
}
func (l *lexer ) unmarshalConnectionInformation () (*ConnectionInformation , error ) {
var err error
var connInfo ConnectionInformation
connInfo .NetworkType , err = l .readField ()
if err != nil {
return nil , err
}
if !anyOf (connInfo .NetworkType , "IN" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , connInfo .NetworkType )
}
connInfo .AddressType , err = l .readField ()
if err != nil {
return nil , err
}
if !anyOf (connInfo .AddressType , "IP4" , "IP6" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , connInfo .AddressType )
}
address , err := l .readField ()
if err != nil {
return nil , err
}
if address != "" {
connInfo .Address = new (Address )
connInfo .Address .Address = address
}
if err := l .nextLine (); err != nil {
return nil , err
}
return &connInfo , nil
}
func unmarshalSessionBandwidth(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
bandwidth , err := unmarshalBandwidth (value )
if err != nil {
return nil , fmt .Errorf ("%w `b=%v`" , errSDPInvalidValue , value )
}
l .desc .Bandwidth = append (l .desc .Bandwidth , *bandwidth )
return s5 , nil
}
func unmarshalBandwidth(value string ) (*Bandwidth , error ) {
parts := strings .Split (value , ":" )
if len (parts ) != 2 {
return nil , fmt .Errorf ("%w `b=%v`" , errSDPInvalidValue , parts )
}
experimental := strings .HasPrefix (parts [0 ], "X-" )
if experimental {
parts [0 ] = strings .TrimPrefix (parts [0 ], "X-" )
} else if !anyOf (parts [0 ], "CT" , "AS" , "TIAS" , "RS" , "RR" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , parts [0 ])
}
bandwidth , err := strconv .ParseUint (parts [1 ], 10 , 64 )
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidNumericValue , parts [1 ])
}
return &Bandwidth {
Experimental : experimental ,
Type : parts [0 ],
Bandwidth : bandwidth ,
}, nil
}
func unmarshalTiming(lex *lexer ) (stateFn , error ) {
var err error
var td TimeDescription
td .Timing .StartTime , err = lex .readUint64Field ()
if err != nil {
return nil , err
}
td .Timing .StopTime , err = lex .readUint64Field ()
if err != nil {
return nil , err
}
if err := lex .nextLine (); err != nil {
return nil , err
}
lex .desc .TimeDescriptions = append (lex .desc .TimeDescriptions , td )
return s9 , nil
}
func unmarshalRepeatTimes(lex *lexer ) (stateFn , error ) {
var err error
var newRepeatTime RepeatTime
latestTimeDesc := &lex .desc .TimeDescriptions [len (lex .desc .TimeDescriptions )-1 ]
field , err := lex .readField ()
if err != nil {
return nil , err
}
newRepeatTime .Interval , err = parseTimeUnits (field )
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , field )
}
field , err = lex .readField ()
if err != nil {
return nil , err
}
newRepeatTime .Duration , err = parseTimeUnits (field )
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , field )
}
for {
field , err := lex .readField ()
if err != nil {
return nil , err
}
if field == "" {
break
}
offset , err := parseTimeUnits (field )
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , field )
}
newRepeatTime .Offsets = append (newRepeatTime .Offsets , offset )
}
if err := lex .nextLine (); err != nil {
return nil , err
}
latestTimeDesc .RepeatTimes = append (latestTimeDesc .RepeatTimes , newRepeatTime )
return s9 , nil
}
func unmarshalTimeZones(lex *lexer ) (stateFn , error ) {
for {
var err error
var timeZone TimeZone
timeZone .AdjustmentTime , err = lex .readUint64Field ()
if err != nil {
return nil , err
}
offset , err := lex .readField ()
if err != nil {
return nil , err
}
if offset == "" {
break
}
timeZone .Offset , err = parseTimeUnits (offset )
if err != nil {
return nil , err
}
lex .desc .TimeZones = append (lex .desc .TimeZones , timeZone )
}
if err := lex .nextLine (); err != nil {
return nil , err
}
return s13 , nil
}
func unmarshalSessionEncryptionKey(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
encryptionKey := EncryptionKey (value )
l .desc .EncryptionKey = &encryptionKey
return s11 , nil
}
func unmarshalSessionAttribute(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
i := strings .IndexRune (value , ':' )
a := l .cache .getSessionAttribute ()
if i > 0 {
a .Key = value [:i ]
a .Value = value [i +1 :]
} else {
a .Key = value
}
return s11 , nil
}
func unmarshalMediaDescription(lex *lexer ) (stateFn , error ) {
populateMediaAttributes (lex .cache , lex .desc )
var newMediaDesc MediaDescription
field , err := lex .readField ()
if err != nil {
return nil , err
}
if !anyOf (field , "audio" , "video" , "text" , "application" , "message" ) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , field )
}
newMediaDesc .MediaName .Media = field
field , err = lex .readField ()
if err != nil {
return nil , err
}
parts := strings .Split (field , "/" )
newMediaDesc .MediaName .Port .Value , err = parsePort (parts [0 ])
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidPortValue , parts [0 ])
}
if len (parts ) > 1 {
var portRange int
portRange , err = strconv .Atoi (parts [1 ])
if err != nil {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , parts )
}
newMediaDesc .MediaName .Port .Range = &portRange
}
field , err = lex .readField ()
if err != nil {
return nil , err
}
for _ , proto := range strings .Split (field , "/" ) {
if !anyOf (
proto ,
"UDP" ,
"RTP" ,
"AVP" ,
"SAVP" ,
"SAVPF" ,
"TLS" ,
"DTLS" ,
"SCTP" ,
"AVPF" ,
"TCP" ,
"MSRP" ,
"BFCP" ,
"UDT" ,
"IX" ,
"MRCPv2" ,
) {
return nil , fmt .Errorf ("%w `%v`" , errSDPInvalidNumericValue , field )
}
newMediaDesc .MediaName .Protos = append (newMediaDesc .MediaName .Protos , proto )
}
for {
field , err = lex .readField ()
if err != nil {
return nil , err
}
if field == "" {
break
}
newMediaDesc .MediaName .Formats = append (newMediaDesc .MediaName .Formats , field )
}
if err := lex .nextLine (); err != nil {
return nil , err
}
lex .desc .MediaDescriptions = append (lex .desc .MediaDescriptions , &newMediaDesc )
return s12 , nil
}
func unmarshalMediaTitle(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
latestMediaDesc := l .desc .MediaDescriptions [len (l .desc .MediaDescriptions )-1 ]
mediaTitle := Information (value )
latestMediaDesc .MediaTitle = &mediaTitle
return s16 , nil
}
func unmarshalMediaConnectionInformation(l *lexer ) (stateFn , error ) {
var err error
latestMediaDesc := l .desc .MediaDescriptions [len (l .desc .MediaDescriptions )-1 ]
latestMediaDesc .ConnectionInformation , err = l .unmarshalConnectionInformation ()
if err != nil {
return nil , err
}
return s15 , nil
}
func unmarshalMediaBandwidth(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
latestMediaDesc := l .desc .MediaDescriptions [len (l .desc .MediaDescriptions )-1 ]
bandwidth , err := unmarshalBandwidth (value )
if err != nil {
return nil , fmt .Errorf ("%w `b=%v`" , errSDPInvalidSyntax , value )
}
latestMediaDesc .Bandwidth = append (latestMediaDesc .Bandwidth , *bandwidth )
return s15 , nil
}
func unmarshalMediaEncryptionKey(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
latestMediaDesc := l .desc .MediaDescriptions [len (l .desc .MediaDescriptions )-1 ]
encryptionKey := EncryptionKey (value )
latestMediaDesc .EncryptionKey = &encryptionKey
return s14 , nil
}
func unmarshalMediaAttribute(l *lexer ) (stateFn , error ) {
value , err := l .readLine ()
if err != nil {
return nil , err
}
i := strings .IndexRune (value , ':' )
a := l .cache .getMediaAttribute ()
if i > 0 {
a .Key = value [:i ]
a .Value = value [i +1 :]
} else {
a .Key = value
}
return s14 , nil
}
func parseTimeUnits(value string ) (num int64 , err error ) {
if len (value ) == 0 {
return 0 , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , value )
}
k := timeShorthand (value [len (value )-1 ])
if k > 0 {
num , err = strconv .ParseInt (value [:len (value )-1 ], 10 , 64 )
} else {
k = 1
num , err = strconv .ParseInt (value , 10 , 64 )
}
if err != nil {
return 0 , fmt .Errorf ("%w `%v`" , errSDPInvalidValue , value )
}
return num * k , nil
}
func timeShorthand(b byte ) int64 {
switch b {
case 'd' :
return 86400
case 'h' :
return 3600
case 'm' :
return 60
case 's' :
return 1
default :
return 0
}
}
func parsePort(value string ) (int , error ) {
port , err := strconv .Atoi (value )
if err != nil {
return 0 , fmt .Errorf ("%w `%v`" , errSDPInvalidPortValue , value )
}
if port < 0 || port > 65535 {
return 0 , fmt .Errorf ("%w -- out of range `%v`" , errSDPInvalidPortValue , port )
}
return port , nil
}
func populateMediaAttributes(c *unmarshalCache , s *SessionDescription ) {
if len (s .MediaDescriptions ) != 0 {
lastMediaDesc := s .MediaDescriptions [len (s .MediaDescriptions )-1 ]
lastMediaDesc .Attributes = c .cloneMediaAttributes ()
}
}
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 .