package tls
import (
"encoding/json"
"errors"
"fmt"
"os"
"github.com/refraction-networking/utls/dicttls"
)
var ErrUnknownExtension = errors .New ("extension name is unknown to the dictionary" )
type ClientHelloSpecJSONUnmarshaler struct {
CipherSuites *CipherSuitesJSONUnmarshaler `json:"cipher_suites"`
CompressionMethods *CompressionMethodsJSONUnmarshaler `json:"compression_methods"`
Extensions *TLSExtensionsJSONUnmarshaler `json:"extensions"`
TLSVersMin uint16 `json:"min_vers,omitempty"`
TLSVersMax uint16 `json:"max_vers,omitempty"`
}
func (chsju *ClientHelloSpecJSONUnmarshaler ) ClientHelloSpec () ClientHelloSpec {
return ClientHelloSpec {
CipherSuites : chsju .CipherSuites .CipherSuites (),
CompressionMethods : chsju .CompressionMethods .CompressionMethods (),
Extensions : chsju .Extensions .Extensions (),
TLSVersMin : chsju .TLSVersMin ,
TLSVersMax : chsju .TLSVersMax ,
}
}
type CipherSuitesJSONUnmarshaler struct {
cipherSuites []uint16
}
func (c *CipherSuitesJSONUnmarshaler ) UnmarshalJSON (jsonStr []byte ) error {
var cipherSuiteNames []string
if err := json .Unmarshal (jsonStr , &cipherSuiteNames ); err != nil {
return err
}
for _ , name := range cipherSuiteNames {
if name == "GREASE" {
c .cipherSuites = append (c .cipherSuites , GREASE_PLACEHOLDER )
continue
}
if id , ok := dicttls .DictCipherSuiteNameIndexed [name ]; ok {
c .cipherSuites = append (c .cipherSuites , id )
} else {
return fmt .Errorf ("unknown cipher suite name: %s" , name )
}
}
return nil
}
func (c *CipherSuitesJSONUnmarshaler ) CipherSuites () []uint16 {
return c .cipherSuites
}
type CompressionMethodsJSONUnmarshaler struct {
compressionMethods []uint8
}
func (c *CompressionMethodsJSONUnmarshaler ) UnmarshalJSON (jsonStr []byte ) error {
var compressionMethodNames []string
if err := json .Unmarshal (jsonStr , &compressionMethodNames ); err != nil {
return err
}
for _ , name := range compressionMethodNames {
if id , ok := dicttls .DictCompMethNameIndexed [name ]; ok {
c .compressionMethods = append (c .compressionMethods , id )
} else {
return fmt .Errorf ("unknown compression method name: %s" , name )
}
}
return nil
}
func (c *CompressionMethodsJSONUnmarshaler ) CompressionMethods () []uint8 {
return c .compressionMethods
}
type TLSExtensionsJSONUnmarshaler struct {
AllowUnknownExt bool
UseRealPSK bool
extensions []TLSExtensionJSON
}
func (e *TLSExtensionsJSONUnmarshaler ) UnmarshalJSON (jsonStr []byte ) error {
var accepters []tlsExtensionJSONAccepter
if err := json .Unmarshal (jsonStr , &accepters ); err != nil {
return err
}
var exts []TLSExtensionJSON = make ([]TLSExtensionJSON , 0 , len (accepters ))
for _ , accepter := range accepters {
if accepter .extNameOnly .Name == "GREASE" {
exts = append (exts , &UtlsGREASEExtension {})
continue
}
if extID , ok := dicttls .DictExtTypeNameIndexed [accepter .extNameOnly .Name ]; !ok {
return fmt .Errorf ("%w: %s" , ErrUnknownExtension , accepter .extNameOnly .Name )
} else {
var ext TLSExtension = ExtensionFromID (extID )
if ext == nil {
if e .AllowUnknownExt {
ext = genericExtension (extID , accepter .extNameOnly .Name )
} else {
return fmt .Errorf ("extension %s (%d) is not JSON compatible" , accepter .extNameOnly .Name , extID )
}
}
switch extID {
case extensionPreSharedKey :
if e .UseRealPSK {
ext = &UtlsPreSharedKeyExtension {}
} else {
ext = &FakePreSharedKeyExtension {}
}
}
if extJsonCompatible , ok := ext .(TLSExtensionJSON ); ok {
exts = append (exts , extJsonCompatible )
} else {
return fmt .Errorf ("extension %s (%d) is not JSON compatible" , accepter .extNameOnly .Name , extID )
}
}
}
for idx , ext := range exts {
if err := json .Unmarshal (accepters [idx ].origJsonInput , ext ); err != nil {
return err
}
}
e .extensions = exts
return nil
}
func (e *TLSExtensionsJSONUnmarshaler ) Extensions () []TLSExtension {
var exts []TLSExtension = make ([]TLSExtension , 0 , len (e .extensions ))
for _ , ext := range e .extensions {
exts = append (exts , ext )
}
return exts
}
func genericExtension (id uint16 , name string ) TLSExtension {
var warningMsg string = "WARNING: extension "
warningMsg += fmt .Sprintf ("%d " , id )
if len (name ) > 0 {
warningMsg += fmt .Sprintf ("(%s) " , name )
}
warningMsg += "is falling back to generic extension"
warningMsg += "\n"
fmt .Fprint (os .Stderr , warningMsg )
return &GenericExtension {Id : id }
}
type tlsExtensionJSONAccepter struct {
extNameOnly struct {
Name string `json:"name"`
}
origJsonInput []byte
}
func (t *tlsExtensionJSONAccepter ) UnmarshalJSON (jsonStr []byte ) error {
t .origJsonInput = make ([]byte , len (jsonStr ))
copy (t .origJsonInput , jsonStr )
return json .Unmarshal (jsonStr , &t .extNameOnly )
}
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 .