shadow1ng/fscan · error

mcs recvData get data error %v

Error message

mcs recvData get data error %v

What it means

During MCS (T.125 Multipoint Communication Service) data reception, the layer header was parsed but reading the remaining data payload bytes from the stream failed. The MCSCONNECT layer emits this on its 'error' channel instead of returning a Go error, and then aborts processing of that packet. It almost always means the underlying transport died or the stream was truncated mid-packet.

Source

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

	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
		}
	}
	if !found {
		glog.Error("mcs receive data for an unconnected layer")
		return
	}
	left, err := core.ReadBytes(int(size), r)
	if err != nil {
		c.Emit("error", errors.New(fmt.Sprintf("mcs recvData get data error %v", err)))
		return
	}
	glog.Debugf("mcs emit channel<%s>:%v", channelName, left)
	c.Emit("sec", channelName, left)
}

func (c *MCSClient) recvChannelJoinConfirm(s []byte) {
	glog.Debug("mcs recvChannelJoinConfirm", hex.EncodeToString(s))
	r := bytes.NewReader(s)
	option, err := core.ReadUInt8(r)
	if err != nil {
		c.Emit("error", err)
		return
	}

	if !readMCSPDUHeader(option, CHANNEL_JOIN_CONFIRM) {
		c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_WAIT_CHANNEL_JOIN_CONFIRM"))
		return

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Check the wrapped error (%v) for net.Error / io.EOF to confirm the transport died, then reconnect and redo the whole X224/MCS/SEC handshake.
  2. Enable glog debug output to see the channel name and confirm where in the session the stream broke.
  3. Verify there are no intermediate proxies/firewalls with idle timeouts shorter than the RDP session keepalive interval.
  4. If it reproduces deterministically at handshake time, capture traffic and check whether the server's MCS SD length exceeds what the client negotiated (block size mismatch).

Example fix

// before
c.Emit("error", errors.New(fmt.Sprintf("mcs recvData get data error %v", err)))
return
// after
// treat it as a disconnect signal and reconnect upstream
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
    c.Emit("disconnect", err)
    return
}
c.Emit("error", fmt.Errorf("mcs recvData get data error %w", err))
Defensive patterns

Strategy: try-catch

Validate before calling

if conn == nil || conn.RemoteAddr() == nil {
    return errors.New("transport not connected before MCS handshake")
}

Try / catch

// listen on the 'error' channel and distinguish transport death
c.On("error", func(err error) {
    if strings.Contains(err.Error(), "mcs recvData get data error") {
        reconnect() // transport-level failure
    }
})

Prevention

When it happens

Trigger: recvData passes the 'found' check (channel layer is connected), then core.ReadBytes(int(size), r) fails because the TCP connection is closed, reset, or returned fewer bytes than the MCS DomainPDU header advertised.

Common situations: Server dropped the RDP connection mid-session; network interruption/NAT timeout while a virtual channel packet is in flight; peer sent a corrupted length field causing the reader to block and then hit a read deadline.

Related errors


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