ginuerzh/gost · warning

bad major version

Error message

bad major version

What it means

ErrBadMajorVersion is returned by the TLS obfs dissector's Parse when the major version byte of a TLS record is not 0x03. All real TLS versions (1.0–1.3) use major version 3 in the record header, so any other value means the stream is not valid TLS.

Source

Thrown at obfs.go:321

	cipherSuites = []uint16{
		0xc02c, 0xc030, 0x009f, 0xcca9, 0xcca8, 0xccaa, 0xc02b, 0xc02f,
		0x009e, 0xc024, 0xc028, 0x006b, 0xc023, 0xc027, 0x0067, 0xc00a,
		0xc014, 0x0039, 0xc009, 0xc013, 0x0033, 0x009d, 0x009c, 0x003d,
		0x003c, 0x0035, 0x002f, 0x00ff,
	}

	compressionMethods = []uint8{0x00}

	algorithms = []uint16{
		0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,
		0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203,
	}

	tlsRecordTypes   = []uint8{0x16, 0x14, 0x16, 0x17}
	tlsVersionMinors = []uint8{0x01, 0x03, 0x03, 0x03}

	ErrBadType         = errors.New("bad type")
	ErrBadMajorVersion = errors.New("bad major version")
	ErrBadMinorVersion = errors.New("bad minor version")
	ErrMaxDataLen      = errors.New("bad tls data len")
)

const (
	tlsRecordStateType = iota
	tlsRecordStateVersion0
	tlsRecordStateVersion1
	tlsRecordStateLength0
	tlsRecordStateLength1
	tlsRecordStateData
)

type obfsTLSParser struct {
	step   uint8
	state  uint8
	length uint16
}

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Confirm the client actually performs a TLS handshake through the obfs layer (otls on both ends).
  2. Check the connecting client is not sending plaintext or another protocol to the otls port.
  3. Filter/drop scanner traffic at the firewall if logs are dominated by probes.
  4. Re-sync or reset the connection when this occurs — the stream is unrecoverable for the dissector.

Example fix

// before (client without TLS obfs connecting to otls server)
dialer: {type: "tcp"}
// after
dialer: {type: "obfs", obfs: "otls"}
Defensive patterns

Strategy: validation

Validate before calling

// check record header major version byte before parsing further
if len(buf) >= 2 && buf[1] != 0x03 {
	return errors.New("not a TLS record: bad major version")
}

Type guard

func hasTLSMajorVersion(hdr []byte) bool {
	return len(hdr) >= 2 && hdr[1] == 0x03
}

Try / catch

n, err := dissector.Parse(conn, buf)
if errors.Is(err, dissector.ErrBadMajorVersion) {
	conn.Close()
	return fmt.Errorf("non-TLS client %s (major version byte != 0x03)", conn.RemoteAddr())
}

Prevention

When it happens

Trigger: During Parse at obfs.go:368, in state tlsRecordStateVersion0, the incoming byte (the record header's major version position) is anything other than 0x03; typically the first record of a connection from a non-TLS client.

Common situations: Plaintext or wrong-protocol clients connecting to an otls-obfuscated port (scanners, probes); obfs method mismatch between client and server; SSLv2-era clients (version byte 0x02); corrupted streams after desync.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/2561f3309d4c66d9. Report an issue: GitHub.