package wire
import (
"errors"
"fmt"
"io"
"github.com/tmc/go-iroh/internal/qng/internal/protocol"
"github.com/tmc/go-iroh/internal/qng/internal/qerr"
"github.com/tmc/go-iroh/internal/qng/quicvarint"
)
var errUnknownFrameType = errors .New ("unknown frame type" )
type FrameParser struct {
ackDelayExponent uint8
supportsDatagrams bool
supportsResetStreamAt bool
supportsAckFrequency bool
supportsMultipath bool
supportsAddressDiscovery bool
supportsNATTraversal bool
ackFrame *AckFrame
}
func NewFrameParser (supportsDatagrams , supportsResetStreamAt , supportsAckFrequency , supportsMultipath bool ) *FrameParser {
return &FrameParser {
supportsDatagrams : supportsDatagrams ,
supportsResetStreamAt : supportsResetStreamAt ,
supportsAckFrequency : supportsAckFrequency ,
supportsMultipath : supportsMultipath ,
ackFrame : &AckFrame {},
}
}
func (p *FrameParser ) ParseType (b []byte , encLevel protocol .EncryptionLevel ) (FrameType , int , error ) {
var parsed int
for len (b ) != 0 {
typ , l , err := quicvarint .Parse (b )
parsed += l
if err != nil {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
ErrorMessage : err .Error(),
}
}
b = b [l :]
if typ == 0x0 {
continue
}
ft := FrameType (typ )
valid := ft .isValidRFC9000 () ||
(p .supportsDatagrams && ft .IsDatagramFrameType ()) ||
(p .supportsResetStreamAt && ft == FrameTypeResetStreamAt ) ||
(p .supportsAckFrequency && (ft == FrameTypeAckFrequency || ft == FrameTypeImmediateAck )) ||
(p .supportsMultipath && ft .isMultipathFrameType ()) ||
(p .supportsAddressDiscovery && ft .isAddressDiscoveryFrameType ()) ||
(p .supportsNATTraversal && ft .isNATTraversalFrameType ())
if !valid {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : typ ,
ErrorMessage : errUnknownFrameType .Error(),
}
}
if !ft .isAllowedAtEncLevel (encLevel ) {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : typ ,
ErrorMessage : fmt .Sprintf ("%d not allowed at encryption level %s" , ft , encLevel ),
}
}
return ft , parsed , nil
}
return 0 , parsed , io .EOF
}
func (p *FrameParser ) ParseStreamFrame (frameType FrameType , data []byte , v protocol .Version ) (*StreamFrame , int , error ) {
frame , n , err := ParseStreamFrame (data , frameType , v )
if err != nil {
return nil , n , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return frame , n , nil
}
func (p *FrameParser ) ParseAckFrame (frameType FrameType , data []byte , encLevel protocol .EncryptionLevel , v protocol .Version ) (*AckFrame , int , error ) {
ackDelayExponent := p .ackDelayExponent
if encLevel != protocol .Encryption1RTT {
ackDelayExponent = protocol .DefaultAckDelayExponent
}
p .ackFrame .Reset ()
l , err := parseAckFrame (p .ackFrame , data , frameType , ackDelayExponent , v )
if err != nil {
return nil , l , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return p .ackFrame , l , nil
}
func (p *FrameParser ) ParseDatagramFrame (frameType FrameType , data []byte , v protocol .Version ) (*DatagramFrame , int , error ) {
f , l , err := parseDatagramFrame (data , frameType , v )
if err != nil {
return nil , 0 , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return f , l , nil
}
func (p *FrameParser ) ParseLessCommonFrame (frameType FrameType , data []byte , v protocol .Version ) (Frame , int , error ) {
var frame Frame
var l int
var err error
switch frameType {
case FrameTypePing :
frame = &PingFrame {}
case FrameTypeResetStream :
frame , l , err = parseResetStreamFrame (data , false , v )
case FrameTypeStopSending :
frame , l , err = parseStopSendingFrame (data , v )
case FrameTypeCrypto :
frame , l , err = parseCryptoFrame (data , v )
case FrameTypeNewToken :
frame , l , err = parseNewTokenFrame (data , v )
case FrameTypeMaxData :
frame , l , err = parseMaxDataFrame (data , v )
case FrameTypeMaxStreamData :
frame , l , err = parseMaxStreamDataFrame (data , v )
case FrameTypeBidiMaxStreams , FrameTypeUniMaxStreams :
frame , l , err = parseMaxStreamsFrame (data , frameType , v )
case FrameTypeDataBlocked :
frame , l , err = parseDataBlockedFrame (data , v )
case FrameTypeStreamDataBlocked :
frame , l , err = parseStreamDataBlockedFrame (data , v )
case FrameTypeBidiStreamBlocked , FrameTypeUniStreamBlocked :
frame , l , err = parseStreamsBlockedFrame (data , frameType , v )
case FrameTypeNewConnectionID :
frame , l , err = parseNewConnectionIDFrame (data , false , v )
case FrameTypeRetireConnectionID :
frame , l , err = parseRetireConnectionIDFrame (data , false , v )
case FrameTypePathChallenge :
frame , l , err = parsePathChallengeFrame (data , v )
case FrameTypePathResponse :
frame , l , err = parsePathResponseFrame (data , v )
case FrameTypeConnectionClose , FrameTypeApplicationClose :
frame , l , err = parseConnectionCloseFrame (data , frameType , v )
case FrameTypeHandshakeDone :
frame = &HandshakeDoneFrame {}
case FrameTypeResetStreamAt :
frame , l , err = parseResetStreamFrame (data , true , v )
case FrameTypeAckFrequency :
frame , l , err = parseAckFrequencyFrame (data , v )
case FrameTypeImmediateAck :
frame = &ImmediateAckFrame {}
case FrameTypeObservedIPv4Addr :
frame , l , err = parseObservedAddrFrame (data , false , v )
case FrameTypeObservedIPv6Addr :
frame , l , err = parseObservedAddrFrame (data , true , v )
case FrameTypePathAck :
frame , l , err = parsePathAckFrame (data , false , p .ackDelayExponent , v )
case FrameTypePathAckECN :
frame , l , err = parsePathAckFrame (data , true , p .ackDelayExponent , v )
case FrameTypePathAbandon :
frame , l , err = parsePathAbandonFrame (data , v )
case FrameTypePathStatusBackup :
frame , l , err = parsePathStatusBackupFrame (data , v )
case FrameTypePathStatusAvailable :
frame , l , err = parsePathStatusAvailableFrame (data , v )
case FrameTypePathNewConnectionID :
frame , l , err = parseNewConnectionIDFrame (data , true , v )
case FrameTypePathRetireConnectionID :
frame , l , err = parseRetireConnectionIDFrame (data , true , v )
case FrameTypeMaxPathID :
frame , l , err = parseMaxPathIDFrame (data , v )
case FrameTypePathsBlocked :
frame , l , err = parsePathsBlockedFrame (data , v )
case FrameTypePathCIDsBlocked :
frame , l , err = parsePathCIDsBlockedFrame (data , v )
case FrameTypeAddIPv4Address :
frame , l , err = parseAddAddressFrame (data , false , v )
case FrameTypeAddIPv6Address :
frame , l , err = parseAddAddressFrame (data , true , v )
case FrameTypeReachOutAtIPv4 :
frame , l , err = parseReachOutFrame (data , false , v )
case FrameTypeReachOutAtIPv6 :
frame , l , err = parseReachOutFrame (data , true , v )
case FrameTypeRemoveAddress :
frame , l , err = parseRemoveAddressFrame (data , v )
default :
err = errUnknownFrameType
}
if err != nil {
return frame , l , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return frame , l , err
}
func (p *FrameParser ) SetAckDelayExponent (exp uint8 ) {
p .ackDelayExponent = exp
}
func (p *FrameParser ) SetSupportsMultipath (supported bool ) {
p .supportsMultipath = supported
}
func (p *FrameParser ) SetSupportsAddressDiscovery (supported bool ) {
p .supportsAddressDiscovery = supported
}
func (p *FrameParser ) SetSupportsNATTraversal (supported bool ) {
p .supportsNATTraversal = supported
}
func replaceUnexpectedEOF(e error ) error {
if e == io .ErrUnexpectedEOF {
return io .EOF
}
return e
}
The pages are generated with Golds v0.8.4 . (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 .