{"record":{"id":"1e6f8ffe69656539","repo":"shadow1ng/fscan","slug":"node-rdp-protocol-t125-mcs-bad-header","errorCode":"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER","errorMessage":"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER","messagePattern":"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/grdp/protocol/t125/mcs.go","lineNumber":375,"sourceCode":"\nfunc (c *MCSClient) sendAttachUserRequest() {\n\tbuff := &bytes.Buffer{}\n\twriteMCSPDUHeader(ATTACH_USER_REQUEST, 0, buff)\n\tc.transport.Write(buff.Bytes())\n}\n\nfunc (c *MCSClient) recvAttachUserConfirm(s []byte) {\n\tglog.Debug(\"mcs recvAttachUserConfirm\", hex.EncodeToString(s))\n\tr := bytes.NewReader(s)\n\n\toption, err := core.ReadUInt8(r)\n\tif err != nil {\n\t\tc.Emit(\"error\", err)\n\t\treturn\n\t}\n\n\tif !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) {\n\t\tc.Emit(\"error\", errors.New(\"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER\"))\n\t\treturn\n\t}\n\n\te, err := per.ReadEnumerates(r)\n\tif err != nil {\n\t\tc.Emit(\"error\", err)\n\t\treturn\n\t}\n\tif e != 0 {\n\t\tc.Emit(\"error\", errors.New(\"NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER'\"))\n\t\treturn\n\t}\n\n\tuserId, _ := per.ReadInteger16(r)\n\tuserId += MCS_USERCHANNEL_BASE\n\tc.userId = userId\n\n\tc.channels = append(c.channels, MCSChannelInfo{userId, \"user\"})","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/libs/grdp/protocol/t125/mcs.go#L357-L393","documentation":"recvAttachUserConfirm expects the first byte of the server reply to be an MCS PDU header whose opcode is ATTACH_USER_CONFIRM (11). readMCSPDUHeader checks (option>>2)==11; if not, this sentinel error is emitted. It means the server responded with a different MCS PDU than the Attach User Confirm the client was waiting for.","triggerScenarios":"The 'data' event handled by recvAttachUserConfirm delivers a buffer whose first byte's high 6 bits are not ATTACH_USER_CONFIRM — e.g. a Disconnect Provider Ultimatum, an error PDU, or out-of-order data arrives.","commonSituations":"Server rejected the earlier Connect Initial/ Erect Domain phase and sends a disconnect PDU instead; TCP framing mismatch so the header byte read is not the PDU start; server under load sends data out of the expected request/confirm sequence.","solutions":["Hex-dump the received buffer and decode the actual MCS opcode from (option>>2) to see what the server sent instead.","Check whether the opcode is DISCONNECT_PROVIDER_ULTIMATUM (8) — that usually indicates the server rejected the connection outright.","Verify the preceding sendAttachUserRequest was actually written (check transport.Write error handling in sendAttachUserRequest).","Retry the connection; transient server-side resets can put the session out of sequence."],"exampleFix":"// before\nif !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) {\n    c.Emit(\"error\", errors.New(\"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER\"))\n    return\n}\n// after\nif readMCSPDUHeader(option, DISCONNECT_PROVIDER_ULTIMATUM) {\n    c.Emit(\"error\", errors.New(\"server sent DISCONNECT_PROVIDER_ULTIMATUM instead of ATTACH_USER_CONFIRM\"))\n    c.transport.Close()\n    return\n}\nif !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) {\n    c.Emit(\"error\", fmt.Errorf(\"NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER: got opcode %d\", option>>2))\n    return\n}","handlingStrategy":"try-catch","validationCode":"// Decode the actual opcode before dispatching handlers:\nop := (buf[0] >> 2)\n// expect op == 11 (ATTACH_USER_CONFIRM); anything else will hit BAD_HEADER","typeGuard":null,"tryCatchPattern":"mcs.On(\"error\", func(err error) {\n    if strings.Contains(err.Error(), \"MCS_BAD_HEADER\") {\n        // server sent an unexpected PDU — close and retry or inspect dump\n    }\n})","preventionTips":["Keep request/response pairing strict: register each Once('data', ...) handler in the right order.","Log the decoded opcode on mismatch to distinguish reject/disconnect PDUs.","Retry connections that fail at this stage — often transient server resets."],"tags":["rdp","mcs","protocol-parsing","unexpected-pdu"],"backgroundTag":"unexpected-response-shape","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}