shadow1ng/fscan · error
unexpected opcode
Error message
unexpected opcode: %d
What it means
Guard in doCassandraAuth: after sending the SASL PLAIN auth token, the next CQL frame carried neither the AUTH_SUCCESS nor the ERROR opcode expected at this point in the handshake. An unexpected protocol opcode means the server response does not match the CQL conversation state.
Solutions
- Note the opcode value in the error and compare with the Cassandra native protocol spec to identify the reply type.
- Confirm the target really is a Cassandra node and no TCP proxy is rewriting the stream.
- Try pinning a different CQL protocol version so framing/opcode negotiation matches the server.
Defensive patterns
Strategy: retry
Try / catch
res := doCassandraAuth(conn, cred, state)
if !res.Success && strings.HasPrefix(res.Error.Error(), "unexpected opcode") {
conn.Close()
conn = dial(...) // fresh connection resyncs framing
} Prevention
- Confirm the target IP:port is a Cassandra native-transport endpoint, not a proxy or another service.
- Pin a mutually supported CQL protocol version.
- Always reconnect (never keep reading) after an unclassifiable frame to avoid desync.
When it happens
Trigger: doCassandraAuth reads the post-SASL response; opcode is not cqlOpAuthOk, cqlOpReady, or cqlOpError — e.g. a protocol violation or an intermediary answering with an unexpected frame.
Common situations: Target is not really Cassandra (a proxy/load balancer speaking something else); protocol version mismatch causing garbled framing; server extension or fork deviating from the native protocol v3/v4 spec.
Related errors
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/9928da084103a38c.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/services/cassandra.go:145
}
// SASL PLAIN: \x00username\x00password
saslToken := []byte("\x00" + cred.Username + "\x00" + cred.Password)
if err := cqlSend(conn, cqlOpAuthRsp, saslToken); err != nil {
state.IncrementTCPFailedPacketCount()
return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
}
opcode, body, err = cqlRecv(conn)
if err != nil {
state.IncrementTCPFailedPacketCount()
return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
}
// AUTH_SUCCESS → 认证成功
// ERROR → 认证失败
if opcode == cqlOpError {
return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: fmt.Errorf("authentication failed: %s", string(body))}
}
if opcode != cqlOpAuthOk && opcode != cqlOpReady {
return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: fmt.Errorf("unexpected opcode: %d", opcode)}
}
}
// Step 4: 发送测试查询
queryBody := cqlLongString("SELECT cluster_name FROM system.local")
// 添加 consistency level (ONE=1)
queryBody = append(queryBody, 0x00, 0x01) // flags=0, consistency=ONE
if err := cqlSend(conn, cqlOpQuery, queryBody); err != nil {
state.IncrementTCPFailedPacketCount()
return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
}
opcode, body, err = cqlRecv(conn)
if err != nil {
state.IncrementTCPFailedPacketCount()
return &AuthResult{Success: false, ErrorType: ErrorTypeNetwork, Error: err}
}
if err := validateCQLQueryResponse(opcode, body); err != nil {
return &AuthResult{Success: false, ErrorType: ErrorTypeAuth, Error: err}View on GitHub (pinned to 95cc12e753)