package t125 import ( "bytes" "errors" "fmt" "io" "log/slog" "reflect" "time" // "git.zeroonesoft.cn/golib/rdplib/plugin/cliprdr" "git.zeroonesoft.cn/golib/rdplib/plugin/drdynvc" "git.zeroonesoft.cn/golib/rdplib/plugin/rail" "git.zeroonesoft.cn/golib/rdplib/plugin/rdpsnd" "git.zeroonesoft.cn/golib/rdplib/core" "git.zeroonesoft.cn/golib/rdplib/emission" "git.zeroonesoft.cn/golib/rdplib/protocol/t125/ber" "git.zeroonesoft.cn/golib/rdplib/protocol/t125/gcc" "git.zeroonesoft.cn/golib/rdplib/protocol/t125/per" ) // take idea from https://github.com/Madnikulin50/gordp // Multiple Channel Service layer type MCSMessage uint8 const ( MCS_TYPE_CONNECT_INITIAL MCSMessage = 0x65 MCS_TYPE_CONNECT_RESPONSE = 0x66 ) type MCSDomainPDU uint16 const ( ERECT_DOMAIN_REQUEST MCSDomainPDU = 1 DISCONNECT_PROVIDER_ULTIMATUM = 8 ATTACH_USER_REQUEST = 10 ATTACH_USER_CONFIRM = 11 CHANNEL_JOIN_REQUEST = 14 CHANNEL_JOIN_CONFIRM = 15 SEND_DATA_REQUEST = 25 SEND_DATA_INDICATION = 26 ) const ( MCS_GLOBAL_CHANNEL_ID uint16 = 1003 MCS_USERCHANNEL_BASE = 1001 ) const ( GLOBAL_CHANNEL_NAME = "global" ) /** * Format MCS PDULayer header packet * @param mcsPdu {integer} * @param options {integer} * @returns {type.UInt8} headers */ func writeMCSPDUHeader(mcsPdu MCSDomainPDU, options uint8, w io.Writer) { core.WriteUInt8((uint8(mcsPdu)<<2)|options, w) } func readMCSPDUHeader(options uint8, mcsPdu MCSDomainPDU) bool { return (options >> 2) == uint8(mcsPdu) } type DomainParameters struct { MaxChannelIds int MaxUserIds int MaxTokenIds int NumPriorities int MinThoughput int MaxHeight int MaxMCSPDUsize int ProtocolVersion int } /** * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 * @returns {asn1.univ.Sequence} */ func NewDomainParameters( maxChannelIds int, maxUserIds int, maxTokenIds int, numPriorities int, minThoughput int, maxHeight int, maxMCSPDUsize int, protocolVersion int) *DomainParameters { return &DomainParameters{maxChannelIds, maxUserIds, maxTokenIds, numPriorities, minThoughput, maxHeight, maxMCSPDUsize, protocolVersion} } func (d *DomainParameters) BER() []byte { buff := &bytes.Buffer{} ber.WriteInteger(d.MaxChannelIds, buff) ber.WriteInteger(d.MaxUserIds, buff) ber.WriteInteger(d.MaxTokenIds, buff) ber.WriteInteger(1, buff) ber.WriteInteger(0, buff) ber.WriteInteger(1, buff) ber.WriteInteger(d.MaxMCSPDUsize, buff) ber.WriteInteger(2, buff) return buff.Bytes() } func ReadDomainParameters(r io.Reader) (*DomainParameters, error) { if !ber.ReadUniversalTag(ber.TAG_SEQUENCE, true, r) { return nil, errors.New("bad BER tags") } d := &DomainParameters{} ber.ReadLength(r) d.MaxChannelIds, _ = ber.ReadInteger(r) d.MaxUserIds, _ = ber.ReadInteger(r) d.MaxTokenIds, _ = ber.ReadInteger(r) ber.ReadInteger(r) ber.ReadInteger(r) ber.ReadInteger(r) d.MaxMCSPDUsize, _ = ber.ReadInteger(r) ber.ReadInteger(r) return d, nil } /** * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 * @param userData {Buffer} * @returns {asn1.univ.Sequence} */ type ConnectInitial struct { CallingDomainSelector []byte CalledDomainSelector []byte UpwardFlag bool TargetParameters DomainParameters MinimumParameters DomainParameters MaximumParameters DomainParameters UserData []byte } func NewConnectInitial(userData []byte) ConnectInitial { return ConnectInitial{[]byte{0x1}, []byte{0x1}, true, *NewDomainParameters(34, 2, 0, 1, 0, 1, 0xffff, 2), *NewDomainParameters(1, 1, 1, 1, 0, 1, 0x420, 2), *NewDomainParameters(0xffff, 0xfc17, 0xffff, 1, 0, 1, 0xffff, 2), userData} } func (c *ConnectInitial) BER() []byte { buff := &bytes.Buffer{} ber.WriteOctetstring(string(c.CallingDomainSelector), buff) ber.WriteOctetstring(string(c.CalledDomainSelector), buff) ber.WriteBoolean(c.UpwardFlag, buff) ber.WriteEncodedDomainParams(c.TargetParameters.BER(), buff) ber.WriteEncodedDomainParams(c.MinimumParameters.BER(), buff) ber.WriteEncodedDomainParams(c.MaximumParameters.BER(), buff) ber.WriteOctetstring(string(c.UserData), buff) return buff.Bytes() } /** * @see http://www.itu.int/rec/T-REC-T.125-199802-I/en page 25 * @returns {asn1.univ.Sequence} */ type ConnectResponse struct { result uint8 calledConnectId int domainParameters *DomainParameters userData []byte } func NewConnectResponse(userData []byte) *ConnectResponse { return &ConnectResponse{0, 0, NewDomainParameters(22, 3, 0, 1, 0, 1, 0xfff8, 2), userData} } func ReadConnectResponse(r io.Reader) (*ConnectResponse, error) { c := &ConnectResponse{} var err error _, err = ber.ReadApplicationTag(MCS_TYPE_CONNECT_RESPONSE, r) if err != nil { return nil, err } c.result, err = ber.ReadEnumerated(r) if err != nil { return nil, err } c.calledConnectId, err = ber.ReadInteger(r) c.domainParameters, err = ReadDomainParameters(r) if err != nil { return nil, err } if !ber.ReadUniversalTag(ber.TAG_OCTET_STRING, false, r) { return nil, errors.New("invalid expected BER tag") } dataLen, _ := ber.ReadLength(r) c.userData, err = core.ReadBytes(dataLen, r) return c, err } type MCSChannelInfo struct { ID uint16 Name string } type MCS struct { emission.Emitter transport core.Transport recvOpCode MCSDomainPDU sendOpCode MCSDomainPDU channels []MCSChannelInfo } func NewMCS(t core.Transport, recvOpCode MCSDomainPDU, sendOpCode MCSDomainPDU) *MCS { m := &MCS{ *emission.NewEmitter(), t, recvOpCode, sendOpCode, []MCSChannelInfo{{MCS_GLOBAL_CHANNEL_ID, GLOBAL_CHANNEL_NAME}}, } m.transport.On("close", func() { m.Emit("close") }).On("error", func(err error) { m.Emit("error", err) }) return m } func (x *MCS) Read(b []byte) (n int, err error) { return x.transport.Read(b) } func (x *MCS) Write(b []byte) (n int, err error) { return x.transport.Write(b) } func (m *MCS) Close() error { return m.transport.Close() } type MCSClient struct { *MCS clientCoreData *gcc.ClientCoreData clientNetworkData *gcc.ClientNetworkData clientSecurityData *gcc.ClientSecurityData // lowColorDepth:SetSessionColorDepth(16/24) 置位, Dynvc-GFX 能力位 // 需要跳过(该端点要求 32bpp 会话) lowColorDepth bool serverCoreData *gcc.ServerCoreData serverNetworkData *gcc.ServerNetworkData serverSecurityData *gcc.ServerSecurityData channelsConnected int userId uint16 nbChannelRequested int pendingJoins int // 并行突发加入后尚未收到确认的 SVC 通道数 messageChannelId uint16 // from SC_MCS_MSGCHANNEL; 0 = not negotiated messageChannelJoined bool bwStartTime time.Time // timestamp of last RDP_BW_START for timeDelta calculation } func NewMCSClient(t core.Transport, kbdLayout uint32, keyboardType uint32, keyboardSubType uint32) *MCSClient { c := &MCSClient{ MCS: NewMCS(t, SEND_DATA_INDICATION, SEND_DATA_REQUEST), clientCoreData: gcc.NewClientCoreData(kbdLayout, keyboardType, keyboardSubType), clientNetworkData: gcc.NewClientNetworkData(), clientSecurityData: gcc.NewClientSecurityData(), userId: 1 + MCS_USERCHANNEL_BASE, } c.transport.On("connect", c.connect) return c } func (c *MCSClient) SetClientDesktop(width, height uint16) { c.clientCoreData.DesktopWidth = width c.clientCoreData.DesktopHeight = height } // SetClientName 覆盖客户端计算机名(ClientCoreData.ClientName,UTF-16LE, // 字段共 32 字节,超长截断)。服务端按该名字管理 \\tsclient 重定向映射, // 同名客户端残留(非正常断开)会让后续同名会话的映射失效——Explorer // 打开 \\tsclient\<名> 报“试图访问无效的地址”且 rdpdr 通道零 IRP // (RDPDR-2)。wasm 下 os.Hostname 回退值恒为 "js",必须每次连接随机化。 func (c *MCSClient) SetClientName(name string) { var buf [32]byte copy(buf[:], core.UnicodeEncode(name)) c.clientCoreData.ClientName = buf } // SetSessionColorDepth 请求会话颜色位数(16/24/32,其它值按 32 处理)。 // 32bpp = highColorDepth 24BPP + WANT_32BPP_SESSION(mstsc 默认); // 16/24bpp 清除 WANT_32BPP_SESSION 并收紧 supportedColorDepths。 // 注意 RDPGFX 会话(RemoteFX/H264 模式)表面恒为 32bpp,此选项只在 // 传统位图管线生效。 // SetSessionColorDepth 请求会话颜色位数(16/24/32,其它值按 32 处理)。 // 实测(Win10 19041)三个坑: // 1. supportedColorDepths 若不含 24/32bpp 支持位,服务器在 MCS 握手 // 阶段直接断连(无 ERRINFO)——支持位恒为全量(与 FreeRDP 一致)。 // 2. 低色深时必须同时清除 SUPPORT_DYNVC_GFX_PROTOCOL(0x200): // 该位声明 32bpp 的 Dynvc-GFX 端点,与 16bpp 会话矛盾,服务器会 // 进入不一致状态(fastpath SURFCMDS 泛滥、音频 DVC 不初始化)。 // SetClientDynvcProtocol 会在低色深时跳过该位(drdynvc 通道保留)。 // 3. 色深协商只通过 highColorDepth + postBeta2ColorDepth + // WANT_32BPP_SESSION 位表达。 // 注意 RDPGFX 会话(RemoteFX/H264 模式)表面恒为 32bpp,此选项只在 // 传统位图管线生效。 func (c *MCSClient) SetSessionColorDepth(bpp int) { d := c.clientCoreData switch bpp { case 16: d.HighColorDepth = gcc.HIGH_COLOR_16BPP d.PostBeta2ColorDepth = gcc.RNS_UD_COLOR_16BPP_565 d.EarlyCapabilityFlags &^= gcc.RNS_UD_CS_WANT_32BPP_SESSION | gcc.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL c.lowColorDepth = true case 24: d.HighColorDepth = gcc.HIGH_COLOR_24BPP d.PostBeta2ColorDepth = gcc.RNS_UD_COLOR_24BPP d.EarlyCapabilityFlags &^= gcc.RNS_UD_CS_WANT_32BPP_SESSION | gcc.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL c.lowColorDepth = true default: // 32bpp:保持 NewClientCoreData 的默认(WANT_32BPP_SESSION) } } func (c *MCSClient) SetClientDynvcProtocol() { // 低色深会话不支持 Dynvc-GFX 端点(见 SetSessionColorDepth), // drdynvc 通道本身保留(剪贴板图片/音频 DVC 仍需要)。 if !c.lowColorDepth { c.clientCoreData.EarlyCapabilityFlags |= gcc.RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL } c.clientNetworkData.AddVirtualChannel(drdynvc.ChannelName, drdynvc.ChannelOption) } func (c *MCSClient) SetClientRemoteProgram() { c.clientNetworkData.AddVirtualChannel(rail.ChannelName, rail.ChannelOption) } func (c *MCSClient) SetClientSoundProtocol() { c.clientNetworkData.AddVirtualChannel(rdpsnd.ChannelName, rdpsnd.ChannelOption) } func (c *MCSClient) SetClientDeviceRedirection() { c.clientNetworkData.AddVirtualChannel("rdpdr", uint32(gcc.CHANNEL_OPTION_INITIALIZED|gcc.CHANNEL_OPTION_ENCRYPT_RDP|gcc.CHANNEL_OPTION_COMPRESS_RDP)) } func (c *MCSClient) SetClientClipboard() { c.clientNetworkData.AddVirtualChannel("cliprdr", uint32(gcc.CHANNEL_OPTION_INITIALIZED|gcc.CHANNEL_OPTION_ENCRYPT_RDP|gcc.CHANNEL_OPTION_COMPRESS_RDP)) } func (c *MCSClient) connect(selectedProtocol uint32) { slog.Debug("connect", "selectedProtocol", selectedProtocol) c.clientCoreData.ServerSelectedProtocol = selectedProtocol slog.Debug("connnect", "clientCoreData", c.clientCoreData) slog.Debug("connect", "clientNetworkData", c.clientNetworkData) slog.Debug("connect", "clientSecurityData", c.clientSecurityData) // sendConnectclientCoreDataInitial userDataBuff := bytes.Buffer{} userDataBuff.Write(c.clientCoreData.Pack()) userDataBuff.Write(c.clientNetworkData.Pack()) userDataBuff.Write(c.clientSecurityData.Pack()) userDataBuff.Write(gcc.PackClientMsgChannelData()) slog.Debug("userData", "data", core.Hex(userDataBuff.Bytes()), "len", len(userDataBuff.Bytes())) ccReq := gcc.MakeConferenceCreateRequest(userDataBuff.Bytes()) slog.Debug("ccReq", "data", core.Hex(ccReq), "len", len(ccReq)) connectInitial := NewConnectInitial(ccReq) connectInitialBerEncoded := connectInitial.BER() dataBuff := &bytes.Buffer{} ber.WriteApplicationTag(uint8(MCS_TYPE_CONNECT_INITIAL), len(connectInitialBerEncoded), dataBuff) dataBuff.Write(connectInitialBerEncoded) slog.Debug("send connet initial", "data", core.Hex(dataBuff.Bytes()), "len", len(dataBuff.Bytes())) _, err := c.transport.Write(dataBuff.Bytes()) if err != nil { c.Emit("error", errors.New(fmt.Sprintf("mcs sendConnectInitial write error %v", err))) return } slog.Debug("mcs wait for data event") c.transport.Once("data", c.recvConnectResponse) } func (c *MCSClient) recvConnectResponse(s []byte) { slog.Debug("mcs recvConnectResponse", "s", core.Hex(s)) cResp, err := ReadConnectResponse(bytes.NewReader(s)) if err != nil { c.Emit("error", errors.New(fmt.Sprintf("ReadConnectResponse %v", err))) return } // record server gcc block serverSettings := gcc.ReadConferenceCreateResponse(cResp.userData) for _, v := range serverSettings { switch v.(type) { case *gcc.ServerSecurityData: c.serverSecurityData = v.(*gcc.ServerSecurityData) case *gcc.ServerCoreData: c.serverCoreData = v.(*gcc.ServerCoreData) case *gcc.ServerNetworkData: c.serverNetworkData = v.(*gcc.ServerNetworkData) case *gcc.ServerMsgChannelData: c.messageChannelId = v.(*gcc.ServerMsgChannelData).MCSChannelId slog.Debug("SC_MCS_MSGCHANNEL", "messageChannelId", c.messageChannelId) default: slog.Warn("recvConnectResponse: unhandled server gcc block", "type", reflect.TypeOf(v)) } } c.sendErectDomainRequest() c.sendAttachUserRequest() c.transport.Once("data", c.recvAttachUserConfirm) } func (c *MCSClient) sendErectDomainRequest() { buff := &bytes.Buffer{} writeMCSPDUHeader(ERECT_DOMAIN_REQUEST, 0, buff) per.WriteInteger(0, buff) per.WriteInteger(0, buff) c.transport.Write(buff.Bytes()) } func (c *MCSClient) sendAttachUserRequest() { buff := &bytes.Buffer{} writeMCSPDUHeader(ATTACH_USER_REQUEST, 0, buff) c.transport.Write(buff.Bytes()) } func (c *MCSClient) recvAttachUserConfirm(s []byte) { slog.Debug("mcs recvAttachUserConfirm", "s", core.Hex(s)) r := bytes.NewReader(s) option, err := core.ReadUInt8(r) if err != nil { c.Emit("error", err) return } if !readMCSPDUHeader(option, ATTACH_USER_CONFIRM) { c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_BAD_HEADER")) return } e, err := per.ReadEnumerates(r) if err != nil { c.Emit("error", err) return } if e != 0 { c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_REJECT_USER'")) return } userId, _ := per.ReadInteger16(r) userId += MCS_USERCHANNEL_BASE c.userId = userId c.channels = append(c.channels, MCSChannelInfo{userId, "user"}) c.connectChannels() } func (c *MCSClient) connectChannels() { slog.Debug("connectChannels", "channelsConnected", c.channelsConnected, "channels", c.channels) if c.channelsConnected < len(c.channels) { // sendChannelJoinRequest c.sendChannelJoinRequest(c.channels[c.channelsConnected].ID) c.transport.Once("data", c.recvChannelJoinConfirm) return } // Join the message channel (for connect-time auto-detection) before SVCs. if c.messageChannelId != 0 && !c.messageChannelJoined { c.messageChannelJoined = true c.sendChannelJoinRequest(c.messageChannelId) c.transport.Once("data", c.recvChannelJoinConfirm) return } if c.nbChannelRequested == 0 && int(c.serverNetworkData.ChannelCount) > 0 { // 并行突发加入全部 SVC 静态通道(mstsc 同款):原先串行逐个等 // 确认,rdpdr 排在队尾,其 join 在服务端 LogonNotify 之后 ~100ms // 才完成——登录期驱动映射(winlogon→drprov 按"当时的设备表"创建 // \\TSCLIENT\<名> 连接)因此永远看不到我们的设备,`dir // \\tsclient\<名>` 恒为"设备没有连接"(2250)。并行发送让 rdpdr // 与其余通道同时就绪,抢在 LogonNotify 之前。 for i := 0; i < int(c.serverNetworkData.ChannelCount); i++ { c.sendChannelJoinRequest(c.serverNetworkData.ChannelIdArray[i]) } c.nbChannelRequested = int(c.serverNetworkData.ChannelCount) c.pendingJoins = int(c.serverNetworkData.ChannelCount) // 单个持久监听 + 计数:emission 对同一条数据事件会触发全部 // 监听器,N×Once 会在首个确认上重复消费(实测导致通道表重复 // 追加与 MCS opcode 错误),故用 Off 在计数归零后摘除。 c.transport.On("data", c.recvChannelJoinConfirm) return } if c.pendingJoins > 0 { // 并行突发确认进行中,由 recvChannelJoinConfirm 计数收尾。 return } c.transport.On("data", c.recvData) // send client and sever gcc informations callback to sec clientData := make([]any, 0) clientData = append(clientData, c.clientCoreData) clientData = append(clientData, c.clientSecurityData) clientData = append(clientData, c.clientNetworkData) serverData := make([]any, 0) serverData = append(serverData, c.serverCoreData) serverData = append(serverData, c.serverSecurityData) c.Emit("connect", clientData, serverData, c.userId, c.channels) } func (c *MCSClient) sendChannelJoinRequest(channelId uint16) { slog.Debug("sendChannelJoinRequest", "channelId", channelId) 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) { 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 } } if !found { if c.messageChannelId != 0 && channelId == c.messageChannelId { data, _ := core.ReadBytes(int(size), r) c.handleAutoDetect(data) return } slog.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 } c.Emit("sec", channelName, left) } func (c *MCSClient) recvChannelJoinConfirm(s []byte) { slog.Debug("recvChannelJoinConfirm", "s", core.Hex(s)) r := bytes.NewReader(s) option, err := core.ReadUInt8(r) if err != nil { return } if !readMCSPDUHeader(option, CHANNEL_JOIN_CONFIRM) { // 并行突发窗口内同监听器会看到数据 PDU:静默忽略(由 recvData // 在突发完成后接管处理)。 return } confirm, _ := per.ReadEnumerates(r) userId, _ := per.ReadInteger16(r) userId += MCS_USERCHANNEL_BASE if c.userId != userId { c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_INVALID_USER_ID")) return } channelId, _ := per.ReadInteger16(r) if (confirm != 0) && (channelId == uint16(MCS_GLOBAL_CHANNEL_ID) || channelId == c.userId) { c.Emit("error", errors.New("NODE_RDP_PROTOCOL_T125_MCS_SERVER_MUST_CONFIRM_STATIC_CHANNEL")) return } if confirm == 0 { for i := 0; i < int(c.serverNetworkData.ChannelCount); i++ { if channelId == c.serverNetworkData.ChannelIdArray[i] { var t MCSChannelInfo t.ID = channelId t.Name = string(c.clientNetworkData.ChannelDefArray[i].Name[:]) c.channels = append(c.channels, t) } } } c.channelsConnected++ if c.pendingJoins > 0 { c.pendingJoins-- if c.pendingJoins > 0 { return } // 全部 SVC 确认到齐:摘除突发期监听器,交还数据泵。 c.transport.Off("data", c.recvChannelJoinConfirm) } c.connectChannels() } // Connect-time auto-detection constants (MS-RDPBCGR 2.2.14). const ( secAutoDetectReq = uint16(0x1000) secAutoDetectRsp = uint16(0x2000) rdpRttRequestConnecttime = uint16(0x1001) rdpBwStartConnecttime = uint16(0x1014) rdpBwPayload = uint16(0x0002) rdpBwStopConnecttime = uint16(0x002B) rdpRttRequest = uint16(0x0001) // continuous RTT request rdpBwStart = uint16(0x0014) // continuous BW start (no response) rdpBwStop = uint16(0x0429) // continuous BW stop typeIDAutodetectResponse = uint8(0x01) rdpRttResponseType = uint16(0x0000) rdpBwResultsConnecttime = uint16(0x0003) rdpBwResults = uint16(0x000B) // continuous BW results ) // handleAutoDetect processes a connect-time auto-detect request from the server // on the message channel. It responds to RTT and BW measurement requests so // that gnome-remote-desktop proceeds to open the audio DVC channels. func (c *MCSClient) handleAutoDetect(data []byte) { r := bytes.NewReader(data) secFlag, _ := core.ReadUint16LE(r) core.ReadUint16LE(r) // secFlagHi if secFlag&secAutoDetectReq == 0 { return } _, _ = core.ReadUInt8(r) // headerLength core.ReadUInt8(r) // headerTypeId seqNum, _ := core.ReadUint16LE(r) reqType, _ := core.ReadUint16LE(r) switch reqType { case rdpRttRequestConnecttime, rdpRttRequest: c.sendAutoDetectResponse(seqNum, rdpRttResponseType, 0) case rdpBwStartConnecttime, rdpBwStart: c.bwStartTime = time.Now() case rdpBwStopConnecttime: elapsed := uint32(time.Since(c.bwStartTime).Milliseconds()) if elapsed == 0 { elapsed = 1 } c.sendAutoDetectResponse(seqNum, rdpBwResultsConnecttime, elapsed) case rdpBwStop: elapsed := uint32(time.Since(c.bwStartTime).Milliseconds()) if elapsed == 0 { elapsed = 1 } c.sendAutoDetectResponse(seqNum, rdpBwResults, elapsed) // rdpBwPayload requires no response } } // sendAutoDetectResponse sends an auto-detect response on the message channel. // timeDelta is 0 for RTT responses (no BW fields); non-zero for BW responses // (timeDelta in milliseconds since the corresponding BW_START was received). func (c *MCSClient) sendAutoDetectResponse(sequenceNumber uint16, responseType uint16, timeDelta uint32) { includeBW := responseType == rdpBwResultsConnecttime || responseType == rdpBwResults headerLength := uint8(6) if includeBW { headerLength = 14 } payload := &bytes.Buffer{} core.WriteUInt16LE(secAutoDetectRsp, payload) core.WriteUInt16LE(0, payload) core.WriteUInt8(headerLength, payload) core.WriteUInt8(typeIDAutodetectResponse, payload) core.WriteUInt16LE(sequenceNumber, payload) core.WriteUInt16LE(responseType, payload) if includeBW { core.WriteUInt32LE(timeDelta, payload) // timeDelta in milliseconds core.WriteUInt32LE(0, payload) // byteCount (no BW_PAYLOAD was sent) } c.transport.Write(c.Pack(payload.Bytes(), c.messageChannelId)) } func (c *MCSClient) Pack(data []byte, channelId uint16) []byte { buff := &bytes.Buffer{} writeMCSPDUHeader(c.sendOpCode, 0, buff) per.WriteInteger16(c.userId-MCS_USERCHANNEL_BASE, buff) per.WriteInteger16(channelId, buff) core.WriteUInt8(0x70, buff) per.WriteLength(len(data), buff) core.WriteBytes(data, buff) return buff.Bytes() } func (c *MCSClient) Write(data []byte) (n int, err error) { data = c.Pack(data, c.channels[0].ID) return c.transport.Write(data) } func (c *MCSClient) SendToChannel(channel string, data []byte) (n int, err error) { channelId := c.channels[0].ID for _, ch := range c.channels { if channel == ch.Name { channelId = ch.ID break } } data = c.Pack(data, channelId) return c.transport.Write(data) }