git.prognetwork.ru/x0r/utls@v1.3.3/u_clienthello_json.go (about)

     1  package tls
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  
     9  	"github.com/gaukas/godicttls"
    10  )
    11  
    12  var ErrUnknownExtension = errors.New("extension name is unknown to the dictionary")
    13  
    14  type ClientHelloSpecJSONUnmarshaler struct {
    15  	CipherSuites       *CipherSuitesJSONUnmarshaler       `json:"cipher_suites"`
    16  	CompressionMethods *CompressionMethodsJSONUnmarshaler `json:"compression_methods"`
    17  	Extensions         *TLSExtensionsJSONUnmarshaler      `json:"extensions"`
    18  	TLSVersMin         uint16                             `json:"min_vers,omitempty"` // optional
    19  	TLSVersMax         uint16                             `json:"max_vers,omitempty"` // optional
    20  }
    21  
    22  func (chsju *ClientHelloSpecJSONUnmarshaler) ClientHelloSpec() ClientHelloSpec {
    23  	return ClientHelloSpec{
    24  		CipherSuites:       chsju.CipherSuites.CipherSuites(),
    25  		CompressionMethods: chsju.CompressionMethods.CompressionMethods(),
    26  		Extensions:         chsju.Extensions.Extensions(),
    27  		TLSVersMin:         chsju.TLSVersMin,
    28  		TLSVersMax:         chsju.TLSVersMax,
    29  	}
    30  }
    31  
    32  type CipherSuitesJSONUnmarshaler struct {
    33  	cipherSuites []uint16
    34  }
    35  
    36  func (c *CipherSuitesJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
    37  	var cipherSuiteNames []string
    38  	if err := json.Unmarshal(jsonStr, &cipherSuiteNames); err != nil {
    39  		return err
    40  	}
    41  
    42  	for _, name := range cipherSuiteNames {
    43  		if name == "GREASE" {
    44  			c.cipherSuites = append(c.cipherSuites, GREASE_PLACEHOLDER)
    45  			continue
    46  		}
    47  
    48  		if id, ok := godicttls.DictCipherSuiteNameIndexed[name]; ok {
    49  			c.cipherSuites = append(c.cipherSuites, id)
    50  		} else {
    51  			return fmt.Errorf("unknown cipher suite name: %s", name)
    52  		}
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  func (c *CipherSuitesJSONUnmarshaler) CipherSuites() []uint16 {
    59  	return c.cipherSuites
    60  }
    61  
    62  type CompressionMethodsJSONUnmarshaler struct {
    63  	compressionMethods []uint8
    64  }
    65  
    66  func (c *CompressionMethodsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
    67  	var compressionMethodNames []string
    68  	if err := json.Unmarshal(jsonStr, &compressionMethodNames); err != nil {
    69  		return err
    70  	}
    71  
    72  	for _, name := range compressionMethodNames {
    73  		if id, ok := godicttls.DictCompMethNameIndexed[name]; ok {
    74  			c.compressionMethods = append(c.compressionMethods, id)
    75  		} else {
    76  			return fmt.Errorf("unknown compression method name: %s", name)
    77  		}
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func (c *CompressionMethodsJSONUnmarshaler) CompressionMethods() []uint8 {
    84  	return c.compressionMethods
    85  }
    86  
    87  type TLSExtensionsJSONUnmarshaler struct {
    88  	extensions []TLSExtensionJSON
    89  }
    90  
    91  func (e *TLSExtensionsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
    92  	var accepters []tlsExtensionJSONAccepter
    93  	if err := json.Unmarshal(jsonStr, &accepters); err != nil {
    94  		return err
    95  	}
    96  
    97  	var exts []TLSExtensionJSON = make([]TLSExtensionJSON, 0, len(accepters))
    98  	for _, accepter := range accepters {
    99  		if accepter.extNameOnly.Name == "GREASE" {
   100  			exts = append(exts, &UtlsGREASEExtension{})
   101  			continue
   102  		}
   103  
   104  		if extID, ok := godicttls.DictExtTypeNameIndexed[accepter.extNameOnly.Name]; !ok {
   105  			return fmt.Errorf("%w: %s", ErrUnknownExtension, accepter.extNameOnly.Name)
   106  		} else {
   107  			// get extension type from ID
   108  			var ext TLSExtension = ExtensionFromID(extID)
   109  			if ext == nil {
   110  				// fallback to generic extension
   111  				ext = genericExtension(extID, accepter.extNameOnly.Name)
   112  			}
   113  
   114  			if extJsonCompatible, ok := ext.(TLSExtensionJSON); ok {
   115  				exts = append(exts, extJsonCompatible)
   116  			} else {
   117  				return fmt.Errorf("extension %d (%s) is not JSON compatible", extID, accepter.extNameOnly.Name)
   118  			}
   119  		}
   120  	}
   121  
   122  	// unmashal extensions
   123  	for idx, ext := range exts {
   124  		// json.Unmarshal will call the UnmarshalJSON method of the extension
   125  		if err := json.Unmarshal(accepters[idx].origJsonInput, ext); err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	e.extensions = exts
   131  	return nil
   132  }
   133  
   134  func (e *TLSExtensionsJSONUnmarshaler) Extensions() []TLSExtension {
   135  	var exts []TLSExtension = make([]TLSExtension, 0, len(e.extensions))
   136  	for _, ext := range e.extensions {
   137  		exts = append(exts, ext)
   138  	}
   139  	return exts
   140  }
   141  
   142  func genericExtension(id uint16, name string) TLSExtension {
   143  	var warningMsg string = "WARNING: extension "
   144  	warningMsg += fmt.Sprintf("%d ", id)
   145  	if len(name) > 0 {
   146  		warningMsg += fmt.Sprintf("(%s) ", name)
   147  	}
   148  	warningMsg += "is falling back to generic extension"
   149  	warningMsg += "\n"
   150  
   151  	fmt.Fprint(os.Stderr, warningMsg)
   152  
   153  	// fallback to generic extension
   154  	return &GenericExtension{Id: id}
   155  }
   156  
   157  type tlsExtensionJSONAccepter struct {
   158  	extNameOnly struct {
   159  		Name string `json:"name"`
   160  	}
   161  	origJsonInput []byte
   162  }
   163  
   164  func (t *tlsExtensionJSONAccepter) UnmarshalJSON(jsonStr []byte) error {
   165  	t.origJsonInput = make([]byte, len(jsonStr))
   166  	copy(t.origJsonInput, jsonStr)
   167  	return json.Unmarshal(jsonStr, &t.extNameOnly)
   168  }