shadow1ng/fscan · error

oracle protocol negotiation expected message 1, got %d

Error message

oracle protocol negotiation expected message 1, got %d

What it means

After the connect packet is sent, the Oracle raw protocol handshake expects the server's first protocol negotiation message to start with message type 1. The server sent a different message byte, so the library aborts with the actual value received. This is a guard against talking to something that is not a normal Oracle TNS listener response flow.

Source

Thrown at plugins/services/oracle_raw.go:969

	if length > 0 {
		_, err = s.getBytes(length)
	}
	return err
}

func (s *oracleSession) protocolNegotiation() (*oracleTCPNego, error) {
	s.reset()
	s.putBytes(1, 6, 0)
	s.putBytes([]byte("OracleClientGo\x00")...)
	if err := s.writeData(); err != nil {
		return nil, err
	}
	msg, err := s.getByte()
	if err != nil {
		return nil, err
	}
	if msg != 1 {
		return nil, fmt.Errorf("oracle protocol negotiation expected message 1, got %d", msg)
	}
	proto, err := s.getByte()
	if err != nil {
		return nil, err
	}
	if proto != 4 && proto != 5 && proto != 6 {
		return nil, errors.New("unsupported oracle server protocol version")
	}
	if _, err = s.getByte(); err != nil {
		return nil, err
	}
	if _, err = s.getNullTermString(50); err != nil {
		return nil, err
	}
	serverCharset, err := s.getInt(2, false, false)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Verify the host/port/SID or service name in the connection string points at a real Oracle listener (test with tnsping or lsnrctl status).
  2. Check whether the listener redirects connections (LOAD_BALANCE/PRECONNECT options) and connect directly to the serving node if redirects are not supported.
  3. Confirm no firewall/proxy is interposing on the connection; capture the first response bytes to see what the server actually returned.

Example fix

// before
ds := "oracle://user:pass@dbhost:1522/SVC"
// after (correct listener port verified via lsnrctl status)
ds := "oracle://user:pass@dbhost:1521/SVC"
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: confirm a live Oracle listener before opening the app connection
conn, err := net.DialTimeout("tcp", "dbhost:1521", 3*time.Second)
if err != nil { log.Fatal("no listener on port") }
conn.Close()

Prevention

When it happens

Trigger: oracleRawAuth -> protocolNegotiation reads the first byte after connecting and it is not 1 (e.g. 4 = a redirect/refuse packet, an error packet, or garbage from a non-Oracle service).

Common situations: Connecting to a port that is not an Oracle listener (wrong host/port config); the listener redirects to another node and the client mishandles the redirect; an old Oracle version or intermediary device answering with a different message order; a health-check probe hitting the DB port.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/60820258968545cf. Report an issue: GitHub.