mirror of
https://github.com/soypat/lneto.git
synced 2026-08-07 00:13:43 +00:00
63f7870fe5
* add handler.encapsulateNode * implement round robin handler approach * simplify round robin implementation * leave TODO * internet.node touch up * fix conflict resolve f-up * replace certain ErrShortBuffer with ErrTruncatedFrame error
149 lines
4.9 KiB
Go
149 lines
4.9 KiB
Go
package tcp
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/soypat/lneto"
|
|
)
|
|
|
|
type OptionKind uint8
|
|
|
|
const (
|
|
OptEnd OptionKind = iota // end of option list
|
|
OptNop // no-operation
|
|
OptMaxSegmentSize // maximum segment size
|
|
OptWindowScale // window scale
|
|
OptSACKPermitted // SACK permitted
|
|
OptSACK // SACK
|
|
OptEcho // echo(obsolete)
|
|
optEchoReply // echo reply(obsolete)
|
|
OptTimestamps // timestamps
|
|
optPOCP // partial order connection permitted(obsolete)
|
|
optPOSP // partial order service profile(obsolete)
|
|
optCC // CC(obsolete)
|
|
optCCnew // CC.new(obsolete)
|
|
optCCecho // CC.echo(obsolete)
|
|
optACR // alternate checksum request(obsolete)
|
|
optACD // alternate checksum data(obsolete)
|
|
optSkeeter // skeeter
|
|
optBubba // bubba
|
|
OptTrailerChecksum // trailer checksum
|
|
optMD5Signature // MD5 signature(obsolete)
|
|
OptSCPSCapabilities // SCPS capabilities
|
|
OptSNA // selective negative acks
|
|
OptRecordBoundaries // record boundaries
|
|
OptCorruptionExperienced // corruption experienced
|
|
OptSNAP // SNAP
|
|
OptUnassigned // unassigned
|
|
OptCompressionFilter // compression filter
|
|
OptQuickStartResponse // quick-start response
|
|
OptUserTimeout // user timeout or unauthorized use
|
|
OptAuthetication // Authentication TCP-AO
|
|
OptMultipath // multipath TCP
|
|
)
|
|
|
|
const (
|
|
OptFastOpenCookie OptionKind = 34 // fast open cookie
|
|
OptEncryptionNegotiation OptionKind = 69 // encryption negotiation
|
|
OptAccurateECN0 OptionKind = 172 // accurate ECN order 0
|
|
OptAccurateECN1 OptionKind = 174 // accurate ECN order 1
|
|
)
|
|
|
|
// IsObsolete returns true if option considered obsolete by newer TCP specifications.
|
|
func (kind OptionKind) IsObsolete() bool {
|
|
if kind.IsDefined() {
|
|
return strings.HasSuffix(kind.String(), "(obsolete)")
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsDefined returns true if the option is a known unreserved option kind.
|
|
func (kind OptionKind) IsDefined() bool {
|
|
return kind <= 30 || kind == 34 || kind == 69 || kind == 172 || kind == 174
|
|
}
|
|
|
|
type OptionCodec struct {
|
|
Flags OptionFlags
|
|
}
|
|
|
|
type OptionFlags uint8
|
|
|
|
const (
|
|
OptFlagSkipSizeValidation OptionFlags = 1 << iota
|
|
OptFlagSkipObsolete
|
|
)
|
|
|
|
func (flags OptionFlags) HasAny(ofTheseFlags OptionFlags) bool {
|
|
return flags&ofTheseFlags != 0
|
|
}
|
|
|
|
func (op OptionCodec) PutOption16(dst []byte, kind OptionKind, v uint16) (int, error) {
|
|
return op.PutOption(dst, kind, byte(v>>8), byte(v))
|
|
}
|
|
|
|
func (op OptionCodec) PutOption32(dst []byte, kind OptionKind, v uint32) (int, error) {
|
|
return op.PutOption(dst, kind, byte(v>>24), byte(v>>16), byte(v>>7), byte(v))
|
|
}
|
|
|
|
func (op OptionCodec) PutOption(dst []byte, kind OptionKind, data ...byte) (int, error) {
|
|
putSize := 2 + len(data)
|
|
if len(dst) < putSize {
|
|
return -1, lneto.ErrShortBuffer
|
|
} else if putSize > 255 {
|
|
return -1, lneto.ErrInvalidLengthField
|
|
} else if kind == OptNop || kind == OptEnd {
|
|
return -1, lneto.ErrInvalidField
|
|
}
|
|
dst[0] = byte(kind)
|
|
dst[1] = byte(putSize)
|
|
copy(dst[2:], data)
|
|
return putSize, nil
|
|
}
|
|
|
|
func (op OptionCodec) ForEachOption(opts []byte, fn func(OptionKind, []byte) error) error {
|
|
off := 0
|
|
skipSizeValidation := op.Flags.HasAny(OptFlagSkipSizeValidation)
|
|
skipObsolete := op.Flags.HasAny(OptFlagSkipObsolete)
|
|
for off < len(opts) && opts[off] != 0 {
|
|
kind := OptionKind(opts[off])
|
|
off++
|
|
if kind == OptNop {
|
|
continue
|
|
}
|
|
if len(opts[off:]) < 1 {
|
|
return lneto.ErrTruncatedFrame
|
|
}
|
|
size := int(opts[off]) // Total option length including kind and length bytes.
|
|
off++
|
|
dataLen := size - 2 // Data bytes after kind and length.
|
|
if dataLen < 0 || len(opts[off:]) < dataLen {
|
|
return lneto.ErrTruncatedFrame
|
|
}
|
|
|
|
if !skipSizeValidation {
|
|
expectSize := -1
|
|
switch kind {
|
|
case OptTimestamps:
|
|
expectSize = 10
|
|
case OptMaxSegmentSize, OptUserTimeout:
|
|
expectSize = 4
|
|
case OptWindowScale:
|
|
expectSize = 3
|
|
case OptSACKPermitted:
|
|
expectSize = 2
|
|
}
|
|
if expectSize != -1 && size != expectSize {
|
|
return lneto.ErrInvalidLengthField
|
|
}
|
|
}
|
|
if !(skipObsolete && kind.IsObsolete()) {
|
|
err := fn(kind, opts[off:off+dataLen])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
off += dataLen
|
|
}
|
|
return nil
|
|
}
|