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
- Confirm the client actually performs a TLS handshake through the obfs layer (otls on both ends).
- Check the connecting client is not sending plaintext or another protocol to the otls port.
- Filter/drop scanner traffic at the firewall if logs are dominated by probes.
- 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
- Verify the peer actually speaks TLS before routing it into the otls dissector.
- Drop non-TLS connections at once; state cannot be recovered.
- Keep obfs configuration symmetric across client and server.
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.