package quic
import (
"encoding/binary"
"errors"
"io"
)
const (
extTypeSNI = 0
extTypeECH = 0xfe0d
)
func findSNIAndECH(data []byte ) (sniPos , sniLen , echPos int , err error ) {
if len (data ) < 4 {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
if data [0 ] != 1 {
return 0 , 0 , 0 , errors .New ("not a ClientHello" )
}
handshakeLen := int (data [1 ])<<16 | int (data [2 ])<<8 | int (data [3 ])
if len (data ) != 4 +handshakeLen {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos := 4
if parsePos +2 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos += 2
if parsePos +32 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos += 32
if parsePos +1 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
sessionIDLen := int (data [parsePos ])
parsePos ++
if parsePos +sessionIDLen > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos += sessionIDLen
if parsePos +2 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
cipherSuitesLen := int (binary .BigEndian .Uint16 (data [parsePos :]))
parsePos += 2
if parsePos +cipherSuitesLen > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos += cipherSuitesLen
if parsePos +1 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
compressionMethodsLen := int (data [parsePos ])
parsePos ++
if parsePos +compressionMethodsLen > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
parsePos += compressionMethodsLen
if parsePos +2 > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
extensionsLen := int (binary .BigEndian .Uint16 (data [parsePos :]))
parsePos += 2
if parsePos +extensionsLen > len (data ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
extensionsStart := parsePos
extensions := data [extensionsStart : extensionsStart +extensionsLen ]
var extPos int
sniPos = -1
echPos = -1
for extPos +4 <= extensionsLen {
extType := binary .BigEndian .Uint16 (extensions [extPos :])
extLen := int (binary .BigEndian .Uint16 (extensions [extPos +2 :]))
if extPos +4 +extLen > extensionsLen {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
switch extType {
case extTypeSNI :
if sniPos != -1 {
return 0 , 0 , 0 , errors .New ("multiple SNI extensions" )
}
sniData := extensions [extPos +4 : extPos +4 +extLen ]
if len (sniData ) < 2 {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
nameListLen := int (binary .BigEndian .Uint16 (sniData ))
if len (sniData ) != 2 +nameListLen {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
listPos := 2
for listPos +3 <= nameListLen +2 {
nameType := sniData [listPos ]
sniLen = int (binary .BigEndian .Uint16 (sniData [listPos +1 :]))
if listPos +3 +sniLen > len (sniData ) {
return 0 , 0 , 0 , io .ErrUnexpectedEOF
}
if nameType == 0 {
sniPos = extensionsStart + extPos + 4 + listPos + 3
break
}
listPos += 3 + sniLen
}
if sniPos == 0 {
return 0 , 0 , 0 , errors .New ("SNI host_name not found" )
}
case extTypeECH :
if echPos != -1 {
return 0 , 0 , 0 , errors .New ("multiple ECH extensions" )
}
echPos = extensionsStart + extPos
}
extPos += 4 + extLen
if sniPos != -1 && echPos != -1 {
break
}
}
return sniPos , sniLen , echPos , nil
}
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 .