shadow1ng/fscan · error
MCS DISCONNECT_PROVIDER_ULTIMATUM
Error message
MCS DISCONNECT_PROVIDER_ULTIMATUM
What it means
In recvData, the steady-state MCS data handler, the first check tests whether the incoming PDU is a DISCONNECT_PROVIDER_ULTIMATUM (opcode 8). If so, the server has torn down the MCS domain/connection, and this error is emitted before closing the transport. It means the server unilaterally disconnected the session.
Source
Thrown at libs/grdp/protocol/t125/mcs.go:450
buff := &bytes.Buffer{}
writeMCSPDUHeader(CHANNEL_JOIN_REQUEST, 0, buff)
per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff)
per.WriteInteger16(channelId, buff)
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 {View on GitHub (pinned to 95cc12e753)
Solutions
- Treat it as a server-initiated disconnect: close client resources and reconnect if the session should persist.
- Implement keep-alive/application-level activity to prevent idle-timeout disconnects.
- Check server event logs for the disconnect reason around the time of failure.
- Add automatic reconnection logic in the consumer of the 'error' event.
Example fix
// before
c.Emit("error", errors.New("MCS DISCONNECT_PROVIDER_ULTIMATUM"))
c.transport.Close()
return
// after
c.Emit("error", errors.New("MCS DISCONNECT_PROVIDER_ULTIMATUM: server disconnected the session"))
c.transport.Close()
c.scheduleReconnect() // consumer-side auto-reconnect Defensive patterns
Strategy: try-catch
Validate before calling
// Detect server disconnect PDU early in the data stream:
if (buf[0]>>2) == 8 { // DISCONNECT_PROVIDER_ULTIMATUM
// server is tearing the session down
} Try / catch
mcs.On("error", func(err error) {
if strings.Contains(err.Error(), "DISCONNECT_PROVIDER_ULTIMATUM") {
// clean up, notify user, and trigger reconnect flow
}
}) Prevention
- Keep the session active to avoid idle-timeout disconnects.
- Monitor server event logs for admin-initiated or policy disconnects.
- Implement automatic reconnection in the client consuming the 'error' event.
When it happens
Trigger: Any 'data' event handled by recvData whose first byte decodes (option>>2) to DISCONNECT_PROVIDER_ULTIMATUM — the server sends this PDU instead of a SEND_DATA_INDICATION.
Common situations: Idle session disconnected by server policy/timeout; administrator logs off or disconnects the session; server crashes or restarts; license expiry; network device (NAT/firewall) idle-timeout prompting server teardown.
Related errors
- bad BER tags
- invalid expected BER tag
- NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER
- NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER
- Invalid expected MCS opcode receive data
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/f5273f60e2dc7ad7.
Report an issue: GitHub.