shadow1ng/fscan · error

Invalid expected MCS opcode receive data

Error message

Invalid expected MCS opcode receive data

What it means

In recvData, after ruling out DISCONNECT_PROVIDER_ULTIMATUM, the header must match c.recvOpCode (normally SEND_DATA_INDICATION). If readMCSPDUHeader(option, c.recvOpCode) fails, the client received an MCS PDU it did not expect in the data phase and emits this error. It indicates a protocol sequence violation or unhandled PDU type mid-session.

Source

Thrown at libs/grdp/protocol/t125/mcs.go:454

	c.transport.Write(buff.Bytes())
}

func (c *MCSClient) recvData(s []byte) {
	glog.Debug("msc on data recvData:", hex.EncodeToString(s))

	r := bytes.NewReader(s)
	option, err := core.ReadUInt8(r)
	if err != nil {
		c.Emit("error", err)
		return
	}

	if readMCSPDUHeader(option, DISCONNECT_PROVIDER_ULTIMATUM) {
		c.Emit("error", errors.New("MCS DISCONNECT_PROVIDER_ULTIMATUM"))
		c.transport.Close()
		return
	} else if !readMCSPDUHeader(option, c.recvOpCode) {
		c.Emit("error", errors.New("Invalid expected MCS opcode receive data"))
		return
	}

	userId, _ := per.ReadInteger16(r)
	userId += MCS_USERCHANNEL_BASE

	channelId, _ := per.ReadInteger16(r)
	per.ReadEnumerates(r)
	size, _ := per.ReadLength(r)
	// channel ID doesn't match a requested layer
	found := false
	channelName := ""
	for _, channel := range c.channels {
		if channel.ID == channelId {
			found = true
			channelName = channel.Name
			break
		}

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Log the unexpected opcode (option>>2) to identify which PDU type arrived and add a handler or skip logic for it.
  2. Ensure all request/response pairs (channel join etc.) complete before registering recvData as the permanent 'data' handler.
  3. Consider buffering/skipping unknown PDUs instead of erroring out, matching FreeRDP's tolerant behavior.
  4. If desynchronization is suspected, dump the hex stream and verify PDU boundaries against T.125 framing.

Example fix

// before
} else if !readMCSPDUHeader(option, c.recvOpCode) {
    c.Emit("error", errors.New("Invalid expected MCS opcode receive data"))
    return
}
// after
} else if !readMCSPDUHeader(option, c.recvOpCode) {
    glog.Warn("unexpected MCS opcode", option>>2, "- skipping")
    return
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-dispatch check of the opcode in the data stream:
op := (buf[0] >> 2)
if op != 26 { // SEND_DATA_INDICATION
    glog.Warn("unexpected MCS opcode during data phase:", op)
}

Try / catch

mcs.On("error", func(err error) {
    if strings.Contains(err.Error(), "Invalid expected MCS opcode") {
        // log opcode, skip or resynchronize instead of hard-failing
    }
})

Prevention

When it happens

Trigger: A 'data' event in the established-session phase carries an MCS PDU whose opcode (option>>2) is neither DISCONNECT_PROVIDER_ULTIMATUM nor SEND_DATA_INDICATION — e.g. a CHANNEL_JOIN_CONFIRM arriving late, or a server-to-client PDU type this fork doesn't handle.

Common situations: Responses to earlier requests arriving after the client switched to recvData (handler registration race between Once('data', ...) and On('data', recvData)); servers sending proprietary/extension PDUs; framing desynchronization after a partial read.

Related errors


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