init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib

This commit is contained in:
w11
2026-09-19 18:55:43 +08:00
commit 1096fbce9d
94 changed files with 36790 additions and 0 deletions
+189
View File
@@ -0,0 +1,189 @@
package ber
import (
"errors"
"fmt"
"io"
"git.zeroonesoft.cn/golib/rdplib/core"
)
const (
CLASS_MASK uint8 = 0xC0
CLASS_UNIV = 0x00
CLASS_APPL = 0x40
CLASS_CTXT = 0x80
CLASS_PRIV = 0xC0
)
const (
PC_MASK uint8 = 0x20
PC_PRIMITIVE = 0x00
PC_CONSTRUCT = 0x20
)
const (
TAG_MASK uint8 = 0x1F
TAG_BOOLEAN = 0x01
TAG_INTEGER = 0x02
TAG_BIT_STRING = 0x03
TAG_OCTET_STRING = 0x04
TAG_OBJECT_IDENFIER = 0x06
TAG_ENUMERATED = 0x0A
TAG_SEQUENCE = 0x10
TAG_SEQUENCE_OF = 0x10
)
func berPC(pc bool) uint8 {
if pc {
return PC_CONSTRUCT
}
return PC_PRIMITIVE
}
func ReadEnumerated(r io.Reader) (uint8, error) {
if !ReadUniversalTag(TAG_ENUMERATED, false, r) {
return 0, errors.New("invalid ber tag")
}
length, err := ReadLength(r)
if err != nil {
return 0, err
}
if length != 1 {
return 0, errors.New(fmt.Sprintf("enumerate size is wrong, get %v, expect 1", length))
}
return core.ReadUInt8(r)
}
func ReadUniversalTag(tag uint8, pc bool, r io.Reader) bool {
bb, _ := core.ReadUInt8(r)
return bb == (CLASS_UNIV|berPC(pc))|(TAG_MASK&tag)
}
func WriteUniversalTag(tag uint8, pc bool, w io.Writer) {
core.WriteUInt8((CLASS_UNIV|berPC(pc))|(TAG_MASK&tag), w)
}
func ReadLength(r io.Reader) (int, error) {
ret := 0
size, _ := core.ReadUInt8(r)
if size&0x80 > 0 {
size = size &^ 0x80
if size == 1 {
r, err := core.ReadUInt8(r)
if err != nil {
return 0, err
}
ret = int(r)
} else if size == 2 {
r, err := core.ReadUint16BE(r)
if err != nil {
return 0, err
}
ret = int(r)
} else {
return 0, errors.New("BER length may be 1 or 2")
}
} else {
ret = int(size)
}
return ret, nil
}
func WriteLength(size int, w io.Writer) {
if size > 0x7f {
core.WriteUInt8(0x82, w)
core.WriteUInt16BE(uint16(size), w)
} else {
core.WriteUInt8(uint8(size), w)
}
}
func ReadInteger(r io.Reader) (int, error) {
if !ReadUniversalTag(TAG_INTEGER, false, r) {
return 0, errors.New("Bad integer tag")
}
size, _ := ReadLength(r)
switch size {
case 1:
num, _ := core.ReadUInt8(r)
return int(num), nil
case 2:
num, _ := core.ReadUint16BE(r)
return int(num), nil
case 3:
integer1, _ := core.ReadUInt8(r)
integer2, _ := core.ReadUint16BE(r)
return int(integer2) + int(uint32(integer1)<<16), nil
case 4:
num, _ := core.ReadUInt32BE(r)
return int(num), nil
default:
return 0, errors.New("wrong size")
}
}
func WriteInteger(n int, w io.Writer) {
WriteUniversalTag(TAG_INTEGER, false, w)
if n <= 0xff {
WriteLength(1, w)
core.WriteUInt8(uint8(n), w)
} else if n <= 0xffff {
WriteLength(2, w)
core.WriteUInt16BE(uint16(n), w)
} else {
WriteLength(4, w)
core.WriteUInt32BE(uint32(n), w)
}
}
func WriteOctetstring(str string, w io.Writer) {
WriteUniversalTag(TAG_OCTET_STRING, false, w)
WriteLength(len(str), w)
core.WriteBytes([]byte(str), w)
}
func WriteBoolean(b bool, w io.Writer) {
bb := uint8(0)
if b {
bb = uint8(0xff)
}
WriteUniversalTag(TAG_BOOLEAN, false, w)
WriteLength(1, w)
core.WriteUInt8(bb, w)
}
func ReadApplicationTag(tag uint8, r io.Reader) (int, error) {
bb, _ := core.ReadUInt8(r)
if tag > 30 {
if bb != (CLASS_APPL|PC_CONSTRUCT)|TAG_MASK {
return 0, errors.New(fmt.Sprintf("ReadApplicationTag error tag=0x%x,bb=0x%x", tag, bb))
}
bb, _ := core.ReadUInt8(r)
if bb != tag {
return 0, errors.New("ReadApplicationTag bad tag")
}
} else {
if bb != (CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag) {
return 0, errors.New(fmt.Sprintf("ReadApplicationTag error valiable length tag=0x%x,bb=0x%x", tag, bb))
}
}
return ReadLength(r)
}
func WriteApplicationTag(tag uint8, size int, w io.Writer) {
if tag > 30 {
core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|TAG_MASK, w)
core.WriteUInt8(tag, w)
WriteLength(size, w)
} else {
core.WriteUInt8((CLASS_APPL|PC_CONSTRUCT)|(TAG_MASK&tag), w)
WriteLength(size, w)
}
}
func WriteEncodedDomainParams(data []byte, w io.Writer) {
WriteUniversalTag(TAG_SEQUENCE, true, w)
WriteLength(len(data), w)
core.WriteBytes(data, w)
}
+671
View File
@@ -0,0 +1,671 @@
package gcc
import (
"bytes"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"io"
"log/slog"
"math/big"
"os"
"github.com/lunixbochs/struc"
"git.zeroonesoft.cn/golib/rdplib/core"
"git.zeroonesoft.cn/golib/rdplib/protocol/t125/per"
)
var t124_02_98_oid = []byte{0, 0, 20, 124, 0, 1}
var h221_cs_key = "Duca"
var h221_sc_key = "McDn"
/**
* @see http://msdn.microsoft.com/en-us/library/cc240509.aspx
*/
type Message uint16
const (
//server -> client
SC_CORE Message = 0x0C01
SC_SECURITY = 0x0C02
SC_NET = 0x0C03
SC_MCS_MSGCHANNEL = 0x0C04 // TS_UD_SC_MCS_MSGCHANNEL
//client -> server
CS_CORE = 0xC001
CS_SECURITY = 0xC002
CS_NET = 0xC003
CS_CLUSTER = 0xC004
CS_MONITOR = 0xC005
CS_MCS_MSGCHANNEL = 0xC006 // TS_UD_CS_MCS_MSGCHANNEL
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type ColorDepth uint16
const (
RNS_UD_COLOR_8BPP ColorDepth = 0xCA01
RNS_UD_COLOR_16BPP_555 = 0xCA02
RNS_UD_COLOR_16BPP_565 = 0xCA03
RNS_UD_COLOR_24BPP = 0xCA04
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type HighColor uint16
const (
HIGH_COLOR_4BPP HighColor = 0x0004
HIGH_COLOR_8BPP = 0x0008
HIGH_COLOR_15BPP = 0x000f
HIGH_COLOR_16BPP = 0x0010
HIGH_COLOR_24BPP = 0x0018
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type Support uint16
const (
RNS_UD_24BPP_SUPPORT uint16 = 0x0001
RNS_UD_16BPP_SUPPORT = 0x0002
RNS_UD_15BPP_SUPPORT = 0x0004
RNS_UD_32BPP_SUPPORT = 0x0008
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type CapabilityFlag uint16
const (
RNS_UD_CS_SUPPORT_ERRINFO_PDU uint16 = 0x0001
RNS_UD_CS_WANT_32BPP_SESSION = 0x0002
RNS_UD_CS_SUPPORT_STATUSINFO_PDU = 0x0004
RNS_UD_CS_STRONG_ASYMMETRIC_KEYS = 0x0008
RNS_UD_CS_UNUSED = 0x0010
RNS_UD_CS_VALID_CONNECTION_TYPE = 0x0020
RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU = 0x0040
RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT = 0x0080
RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL = 0x0100
RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE = 0x0200
RNS_UD_CS_SUPPORT_HEARTBEAT_PDU = 0x0400
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type ConnectionType uint8
const (
CONNECTION_TYPE_MODEM ConnectionType = 0x01
CONNECTION_TYPE_BROADBAND_LOW = 0x02
CONNECTION_TYPE_SATELLITEV = 0x03
CONNECTION_TYPE_BROADBAND_HIGH = 0x04
CONNECTION_TYPE_WAN = 0x05
CONNECTION_TYPE_LAN = 0x06
CONNECTION_TYPE_AUTODETECT = 0x07
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
*/
type VERSION uint32
const (
RDP_VERSION_4 VERSION = 0x00080001
RDP_VERSION_5_PLUS = 0x00080004
RDP_VERSION_10 = 0x00080005
RDP_VERSION_10_1 = 0x00080006
RDP_VERSION_10_2 = 0x00080007
)
type Sequence uint16
const (
RNS_UD_SAS_DEL Sequence = 0xAA03
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
*/
type EncryptionMethod uint32
const (
ENCRYPTION_FLAG_40BIT uint32 = 0x00000001
ENCRYPTION_FLAG_128BIT = 0x00000002
ENCRYPTION_FLAG_56BIT = 0x00000008
FIPS_ENCRYPTION_FLAG = 0x00000010
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
*/
type EncryptionLevel uint32
const (
ENCRYPTION_LEVEL_NONE EncryptionLevel = 0x00000000
ENCRYPTION_LEVEL_LOW = 0x00000001
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE = 0x00000002
ENCRYPTION_LEVEL_HIGH = 0x00000003
ENCRYPTION_LEVEL_FIPS = 0x00000004
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240513.aspx
*/
type ChannelOptions uint32
const (
CHANNEL_OPTION_INITIALIZED ChannelOptions = 0x80000000
CHANNEL_OPTION_ENCRYPT_RDP = 0x40000000
CHANNEL_OPTION_ENCRYPT_SC = 0x20000000
CHANNEL_OPTION_ENCRYPT_CS = 0x10000000
CHANNEL_OPTION_PRI_HIGH = 0x08000000
CHANNEL_OPTION_PRI_MED = 0x04000000
CHANNEL_OPTION_PRI_LOW = 0x02000000
CHANNEL_OPTION_COMPRESS_RDP = 0x00800000
CHANNEL_OPTION_COMPRESS = 0x00400000
CHANNEL_OPTION_SHOW_PROTOCOL = 0x00200000
REMOTE_CONTROL_PERSISTENT = 0x00100000
)
/**
* IBM_101_102_KEYS is the most common keyboard type
*/
type KeyboardType uint32
const (
KT_IBM_PC_XT_83_KEY KeyboardType = 0x00000001
KT_OLIVETTI = 0x00000002
KT_IBM_PC_AT_84_KEY = 0x00000003
KT_IBM_101_102_KEYS = 0x00000004
KT_NOKIA_1050 = 0x00000005
KT_NOKIA_9140 = 0x00000006
KT_JAPANESE = 0x00000007
)
/**
* @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx
*/
type KeyboardLayout uint32
const (
ARABIC KeyboardLayout = 0x00000401
BULGARIAN = 0x00000402
CHINESE_US_KEYBOARD = 0x00000404
CZECH = 0x00000405
DANISH = 0x00000406
GERMAN = 0x00000407
GREEK = 0x00000408
US = 0x00000409
SPANISH = 0x0000040a
FINNISH = 0x0000040b
FRENCH = 0x0000040c
HEBREW = 0x0000040d
HUNGARIAN = 0x0000040e
ICELANDIC = 0x0000040f
ITALIAN = 0x00000410
JAPANESE = 0x00000411
KOREAN = 0x00000412
DUTCH = 0x00000413
NORWEGIAN = 0x00000414
)
/**
* @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
*/
type CertificateType uint32
const (
CERT_CHAIN_VERSION_1 CertificateType = 0x00000001
CERT_CHAIN_VERSION_2 = 0x00000002
)
type ChannelDef struct {
Name string `struc:"little"`
Options uint32 `struc:"little"`
}
type ClientCoreData struct {
RdpVersion VERSION `struc:"uint32,little"`
DesktopWidth uint16 `struc:"little"`
DesktopHeight uint16 `struc:"little"`
ColorDepth ColorDepth `struc:"little"`
SasSequence Sequence `struc:"little"`
KbdLayout KeyboardLayout `struc:"little"`
ClientBuild uint32 `struc:"little"`
ClientName [32]byte `struc:"[32]byte"`
KeyboardType uint32 `struc:"little"`
KeyboardSubType uint32 `struc:"little"`
KeyboardFnKeys uint32 `struc:"little"`
ImeFileName [64]byte `struc:"[64]byte"`
PostBeta2ColorDepth ColorDepth `struc:"little"`
ClientProductId uint16 `struc:"little"`
SerialNumber uint32 `struc:"little"`
HighColorDepth HighColor `struc:"little"`
SupportedColorDepths uint16 `struc:"little"`
EarlyCapabilityFlags uint16 `struc:"little"`
ClientDigProductId [64]byte `struc:"[64]byte"`
ConnectionType uint8 `struc:"uint8"`
Pad1octet uint8 `struc:"uint8"`
ServerSelectedProtocol uint32 `struc:"little"`
}
func NewClientCoreData(kbdLayout uint32, keyboardType uint32, keyboardSubType uint32) *ClientCoreData {
name, _ := os.Hostname()
var ClientName [32]byte
copy(ClientName[:], core.UnicodeEncode(name)[:])
return &ClientCoreData{
RDP_VERSION_10_2, 1280, 800, RNS_UD_COLOR_8BPP,
RNS_UD_SAS_DEL, KeyboardLayout(kbdLayout), 22621, ClientName, keyboardType,
keyboardSubType, 12, [64]byte{}, RNS_UD_COLOR_8BPP, 1, 0, HIGH_COLOR_24BPP,
RNS_UD_15BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_32BPP_SUPPORT,
RNS_UD_CS_SUPPORT_ERRINFO_PDU | RNS_UD_CS_WANT_32BPP_SESSION | RNS_UD_CS_VALID_CONNECTION_TYPE | RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT,
[64]byte{}, uint8(CONNECTION_TYPE_LAN), 0, 0}
}
func (data *ClientCoreData) Pack() []byte {
buff := &bytes.Buffer{}
core.WriteUInt16LE(CS_CORE, buff) // 01C0
core.WriteUInt16LE(0xd8, buff) // d800
struc.Pack(buff, data)
return buff.Bytes()
}
type ClientNetworkData struct {
ChannelCount uint32
ChannelDefArray []ChannelDef
}
func NewClientNetworkData() *ClientNetworkData {
n := &ClientNetworkData{ChannelDefArray: make([]ChannelDef, 0, 100)}
/*var d1 ChannelDef
d1.Name = plugin.RDPDR_SVC_CHANNEL_NAME
d1.Options = uint32(CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
CHANNEL_OPTION_COMPRESS_RDP)
n.ChannelDefArray = append(n.ChannelDefArray, d1)
var d2 ChannelDef
d2.Name = plugin.RDPSND_SVC_CHANNEL_NAME
d2.Options = uint32(CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL)
n.ChannelDefArray = append(n.ChannelDefArray, d2)*/
return n
}
func (n *ClientNetworkData) AddVirtualChannel(name string, option uint32) {
var d ChannelDef
d.Name = name
d.Options = option
n.ChannelDefArray = append(n.ChannelDefArray, d)
n.ChannelCount++
}
func (n *ClientNetworkData) Pack() []byte {
buff := &bytes.Buffer{}
core.WriteUInt16LE(CS_NET, buff) // type
length := uint16(n.ChannelCount*12 + 8)
core.WriteUInt16LE(length, buff) // len 8
core.WriteUInt32LE(n.ChannelCount, buff)
for i := 0; i < int(n.ChannelCount); i++ {
v := n.ChannelDefArray[i]
var name [8]byte
copy(name[:], v.Name)
core.WriteBytes(name[:], buff)
core.WriteUInt32LE(v.Options, buff)
}
return buff.Bytes()
}
type ClientSecurityData struct {
EncryptionMethods uint32
ExtEncryptionMethods uint32
}
func NewClientSecurityData() *ClientSecurityData {
return &ClientSecurityData{
ENCRYPTION_FLAG_40BIT | ENCRYPTION_FLAG_56BIT | ENCRYPTION_FLAG_128BIT,
00}
}
func (d *ClientSecurityData) Pack() []byte {
buff := &bytes.Buffer{}
core.WriteUInt16LE(CS_SECURITY, buff) // type
core.WriteUInt16LE(0x0c, buff) // len 12
core.WriteUInt32LE(d.EncryptionMethods, buff)
core.WriteUInt32LE(d.ExtEncryptionMethods, buff)
return buff.Bytes()
}
type RSAPublicKey struct {
Magic uint32 `struc:"little"` //0x31415352
Keylen uint32 `struc:"little,sizeof=Modulus"`
Bitlen uint32 `struc:"little"`
Datalen uint32 `struc:"little"`
PubExp uint32 `struc:"little"`
Modulus []byte `struc:"little"`
Padding []byte `struc:"[8]byte"`
}
type ProprietaryServerCertificate struct {
DwSigAlgId uint32 `struc:"little"` //0x00000001
DwKeyAlgId uint32 `struc:"little"` //0x00000001
PublicKeyBlobType uint16 `struc:"little"` //0x0006
PublicKeyBlobLen uint16 `struc:"little,sizeof=PublicKeyBlob"`
PublicKeyBlob RSAPublicKey `struc:"little"`
SignatureBlobType uint16 `struc:"little"` //0x0008
SignatureBlobLen uint16 `struc:"little,sizeof=SignatureBlob"`
SignatureBlob []byte `struc:"little"`
//PaddingLen uint16 `struc:"little,sizeof=Padding,skip"`
Padding []byte `struc:"[8]byte"`
}
func (p *ProprietaryServerCertificate) GetPublicKey() (*rsa.PublicKey, error) {
b := new(big.Int).SetBytes(core.Reverse(p.PublicKeyBlob.Modulus))
e := new(big.Int).SetInt64(int64(p.PublicKeyBlob.PubExp))
return &rsa.PublicKey{N: b, E: int(e.Int64())}, nil
}
func (p *ProprietaryServerCertificate) Verify() bool {
return true
}
func (p *ProprietaryServerCertificate) Encrypt() []byte {
//todo
return nil
}
func (p *ProprietaryServerCertificate) Unpack(r io.Reader) error {
p.DwSigAlgId, _ = core.ReadUInt32LE(r)
p.DwKeyAlgId, _ = core.ReadUInt32LE(r)
p.PublicKeyBlobType, _ = core.ReadUint16LE(r)
p.PublicKeyBlobLen, _ = core.ReadUint16LE(r)
var b RSAPublicKey
b.Magic, _ = core.ReadUInt32LE(r)
b.Keylen, _ = core.ReadUInt32LE(r)
b.Bitlen, _ = core.ReadUInt32LE(r)
b.Datalen, _ = core.ReadUInt32LE(r)
b.PubExp, _ = core.ReadUInt32LE(r)
b.Modulus, _ = core.ReadBytes(int(b.Keylen)-8, r)
b.Padding, _ = core.ReadBytes(8, r)
p.PublicKeyBlob = b
p.SignatureBlobType, _ = core.ReadUint16LE(r)
p.SignatureBlobLen, _ = core.ReadUint16LE(r)
p.SignatureBlob, _ = core.ReadBytes(int(p.SignatureBlobLen)-8, r)
p.Padding, _ = core.ReadBytes(8, r)
return nil
}
type CertBlob struct {
CbCert uint32 `struc:"little,sizeof=AbCert"`
AbCert []byte `struc:"little"`
}
type X509CertificateChain struct {
NumCertBlobs uint32 `struc:"little,sizeof=CertBlobArray"`
CertBlobArray []CertBlob `struc:"little"`
Padding []byte `struc:"[12]byte"`
}
func (x *X509CertificateChain) GetPublicKey() (*rsa.PublicKey, error) {
data := x.CertBlobArray[len(x.CertBlobArray)-1].AbCert
cert, err := x509.ParseCertificate(data)
if err != nil {
slog.Error("X509 ParseCertificate", "err", err)
return nil, err
}
var rsaPublicKey *rsa.PublicKey
if cert.PublicKey == nil {
var pubKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString
}
_, err = asn1.Unmarshal(cert.RawSubjectPublicKeyInfo, &pubKeyInfo)
if err != nil {
return nil, err
}
rsaPublicKey, err = x509.ParsePKCS1PublicKey(pubKeyInfo.SubjectPublicKey.Bytes)
if err != nil {
return nil, err
}
} else {
rsaPublicKey = cert.PublicKey.(*rsa.PublicKey)
}
return rsaPublicKey, nil
}
func (x *X509CertificateChain) Verify() bool {
return true
}
func (x *X509CertificateChain) Encrypt() []byte {
//todo
return nil
}
func (x *X509CertificateChain) Unpack(r io.Reader) error {
return struc.Unpack(r, x)
}
type ServerCoreData struct {
RdpVersion VERSION `struc:"uint32,little"`
ClientRequestedProtocol uint32 `struc:"little"`
EarlyCapabilityFlags uint32 `struc:"little"`
}
func NewServerCoreData() *ServerCoreData {
return &ServerCoreData{
RDP_VERSION_5_PLUS, 0, 0}
}
func (d *ServerCoreData) Serialize() []byte {
return []byte{}
}
func (d *ServerCoreData) ScType() Message {
return SC_CORE
}
func (d *ServerCoreData) Unpack(r io.Reader) error {
version, _ := core.ReadUInt32LE(r)
d.RdpVersion = VERSION(version)
d.ClientRequestedProtocol, _ = core.ReadUInt32LE(r)
d.EarlyCapabilityFlags, _ = core.ReadUInt32LE(r)
return nil
//return struc.Unpack(r, d)
}
type ServerNetworkData struct {
MCSChannelId uint16 `struc:"little"`
ChannelCount uint16 `struc:"little,sizeof=ChannelIdArray"`
ChannelIdArray []uint16 `struc:"little"`
}
func NewServerNetworkData() *ServerNetworkData {
return &ServerNetworkData{}
}
func (d *ServerNetworkData) ScType() Message {
return SC_NET
}
func (d *ServerNetworkData) Unpack(r io.Reader) error {
return struc.Unpack(r, d)
}
type CertData interface {
GetPublicKey() (*rsa.PublicKey, error)
Verify() bool
Unpack(io.Reader) error
}
type ServerCertificate struct {
DwVersion uint32
CertData CertData
}
func (sc *ServerCertificate) Unpack(r io.Reader) error {
sc.DwVersion, _ = core.ReadUInt32LE(r)
var cd CertData
switch CertificateType(sc.DwVersion & 0x7fffffff) {
case CERT_CHAIN_VERSION_1:
slog.Debug("ProprietaryServerCertificate")
cd = &ProprietaryServerCertificate{}
case CERT_CHAIN_VERSION_2:
slog.Debug("X509CertificateChain")
cd = &X509CertificateChain{}
default:
slog.Error("Unpack", "Unsupported version", sc.DwVersion&0x7fffffff)
return errors.New("Unsupported version")
}
if cd != nil {
err := cd.Unpack(r)
if err != nil {
slog.Error("Unpack", "err", err)
return err
}
}
sc.CertData = cd
return nil
}
type ServerSecurityData struct {
EncryptionMethod uint32 `struc:"little"`
EncryptionLevel uint32 `struc:"little"`
ServerRandomLen uint32 //0x00000020
ServerCertLen uint32
ServerRandom []byte
ServerCertificate ServerCertificate
}
func NewServerSecurityData() *ServerSecurityData {
return &ServerSecurityData{
0, 0, 0x00000020, 0, []byte{}, ServerCertificate{}}
}
func (d *ServerSecurityData) ScType() Message {
return SC_SECURITY
}
func (s *ServerSecurityData) Unpack(r io.Reader) error {
s.EncryptionMethod, _ = core.ReadUInt32LE(r)
s.EncryptionLevel, _ = core.ReadUInt32LE(r)
if !(s.EncryptionMethod == 0 && s.EncryptionLevel == 0) {
s.ServerRandomLen, _ = core.ReadUInt32LE(r)
s.ServerCertLen, _ = core.ReadUInt32LE(r)
s.ServerRandom, _ = core.ReadBytes(int(s.ServerRandomLen), r)
var sc ServerCertificate
data, _ := core.ReadBytes(int(s.ServerCertLen), r)
rd := bytes.NewReader(data)
err := sc.Unpack(rd)
if err != nil {
return err
}
s.ServerCertificate = sc
}
return nil
}
// ServerMsgChannelData holds the message channel ID allocated by the server
// (TS_UD_SC_MCS_MSGCHANNEL). Used for connect-time network auto-detection.
type ServerMsgChannelData struct {
MCSChannelId uint16
}
func (d *ServerMsgChannelData) ScType() Message {
return SC_MCS_MSGCHANNEL
}
func (d *ServerMsgChannelData) Unpack(r io.Reader) error {
var err error
d.MCSChannelId, err = core.ReadUint16LE(r)
return err
}
// PackClientMsgChannelData serialises the TS_UD_CS_MCS_MSGCHANNEL block.
// Advertising this block requests that the server allocate a dedicated message
// channel for connect-time network auto-detection (RTT/BW measurements).
func PackClientMsgChannelData() []byte {
buff := &bytes.Buffer{}
core.WriteUInt16LE(CS_MCS_MSGCHANNEL, buff) // type 0xC006
core.WriteUInt16LE(0x08, buff) // length = 8
core.WriteUInt32LE(0, buff) // flags = 0
return buff.Bytes()
}
func MakeConferenceCreateRequest(userData []byte) []byte {
buff := &bytes.Buffer{}
per.WriteChoice(0, buff) // 00
per.WriteObjectIdentifier(t124_02_98_oid, buff) // 05:00:14:7c:00:01
per.WriteLength(len(userData)+14, buff)
per.WriteChoice(0, buff) // 00
per.WriteSelection(0x08, buff) // 08
per.WriteNumericString("1", 1, buff) // 00 10
per.WritePadding(1, buff) // 00
per.WriteNumberOfSet(1, buff) // 01
per.WriteChoice(0xc0, buff) // c0
per.WriteOctetStream(h221_cs_key, 4, buff) // 00 44:75:63:61
per.WriteOctetStream(string(userData), 0, buff)
return buff.Bytes()
}
type ScData interface {
ScType() Message
Unpack(io.Reader) error
}
func ReadConferenceCreateResponse(data []byte) []any {
ret := make([]any, 0, 3)
r := bytes.NewReader(data)
per.ReadChoice(r)
if !per.ReadObjectIdentifier(r, t124_02_98_oid) {
slog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124")
return ret
}
per.ReadLength(r)
per.ReadChoice(r)
per.ReadInteger16(r)
per.ReadInteger(r)
per.ReadEnumerates(r)
per.ReadNumberOfSet(r)
per.ReadChoice(r)
if !per.ReadOctetStream(r, h221_sc_key, 4) {
slog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY")
return ret
}
ln, _ := per.ReadLength(r)
for ln > 0 {
t, _ := core.ReadUint16LE(r)
l, _ := core.ReadUint16LE(r)
dataBytes, _ := core.ReadBytes(int(l)-4, r)
ln = ln - l
var d ScData
switch Message(t) {
case SC_CORE:
d = &ServerCoreData{}
case SC_SECURITY:
d = &ServerSecurityData{}
case SC_NET:
d = &ServerNetworkData{}
case SC_MCS_MSGCHANNEL:
d = &ServerMsgChannelData{}
default:
slog.Debug("ReadConferenceCreateResponse: ignoring unknown block", "type", t)
continue
}
if d != nil {
r := bytes.NewReader(dataBytes)
err := d.Unpack(r)
if err != nil {
slog.Warn("ReadConferenceCreateResponse", "err", err)
}
ret = append(ret, d)
}
}
return ret
}
+753
View File
@@ -0,0 +1,753 @@
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)
}
+211
View File
@@ -0,0 +1,211 @@
package per
import (
"io"
"log/slog"
"git.zeroonesoft.cn/golib/rdplib/core"
)
// zeroPad is a shared zero buffer used by WritePadding to avoid per-call heap
// allocations. Writing is done in chunks up to len(zeroPad).
var zeroPad [256]byte
func ReadEnumerates(r io.Reader) (uint8, error) {
return core.ReadUInt8(r)
}
func WriteInteger(n int, w io.Writer) {
if n <= 0xff {
WriteLength(1, w)
core.WriteUInt8(uint8(n), w)
} else if n <= 0xffff {
WriteLength(2, w)
core.WriteUInt16BE(uint16(n), w)
} else {
WriteLength(4, w)
core.WriteUInt32BE(uint32(n), w)
}
}
func ReadInteger16(r io.Reader) (uint16, error) {
return core.ReadUint16BE(r)
}
func WriteInteger16(value uint16, w io.Writer) {
core.WriteUInt16BE(value, w)
}
/**
* @param choice {integer}
* @returns {type.UInt8} choice per encoded
*/
func WriteChoice(choice uint8, w io.Writer) {
core.WriteUInt8(choice, w)
}
/**
* @param value {raw} value to convert to per format
* @returns type objects per encoding value
*/
func WriteLength(value int, w io.Writer) {
if value > 0x7f {
core.WriteUInt16BE(uint16(value|0x8000), w)
} else {
core.WriteUInt8(uint8(value), w)
}
}
func ReadLength(r io.Reader) (uint16, error) {
b, err := core.ReadUInt8(r)
if err != nil {
return 0, nil
}
var size uint16
if b&0x80 > 0 {
b = b &^ 0x80
size = uint16(b) << 8
left, _ := core.ReadUInt8(r)
size += uint16(left)
} else {
size = uint16(b)
}
return size, nil
}
/**
* @param oid {array} oid to write
* @returns {type.Component} per encoded object identifier
*/
func WriteObjectIdentifier(oid []byte, w io.Writer) {
core.WriteUInt8(5, w)
core.WriteByte((oid[0]<<4)&(oid[1]&0x0f), w)
core.WriteByte(oid[2], w)
core.WriteByte(oid[3], w)
core.WriteByte(oid[4], w)
core.WriteByte(oid[5], w)
}
/**
* @param selection {integer}
* @returns {type.UInt8} per encoded selection
*/
func WriteSelection(selection uint8, w io.Writer) {
core.WriteUInt8(selection, w)
}
func WriteNumericString(s string, minValue int, w io.Writer) {
length := len(s)
mLength := minValue
if length >= minValue {
mLength = length - minValue
}
WriteLength(mLength, w)
var buf [1]byte
for i := 0; i < length; i += 2 {
c1 := int(s[i])
c2 := 0x30
if i+1 < length {
c2 = int(s[i+1])
}
c1 = (c1 - 0x30) % 10
c2 = (c2 - 0x30) % 10
buf[0] = uint8((c1 << 4) | c2)
w.Write(buf[:])
}
}
func WritePadding(length int, w io.Writer) {
for length > 0 {
n := length
if n > len(zeroPad) {
n = len(zeroPad)
}
w.Write(zeroPad[:n])
length -= n
}
}
func WriteNumberOfSet(n int, w io.Writer) {
core.WriteUInt8(uint8(n), w)
}
/**
* @param oStr {String}
* @param minValue {integer} default 0
* @returns {type.Component} per encoded octet stream
*/
func WriteOctetStream(oStr string, minValue int, w io.Writer) {
length := len(oStr)
mlength := minValue
if length-minValue >= 0 {
mlength = length - minValue
}
WriteLength(mlength, w)
io.WriteString(w, oStr)
}
func ReadChoice(r io.Reader) uint8 {
choice, _ := core.ReadUInt8(r)
return choice
}
func ReadNumberOfSet(r io.Reader) uint8 {
choice, _ := core.ReadUInt8(r)
return choice
}
func ReadInteger(r io.Reader) uint32 {
size, _ := ReadLength(r)
switch size {
case 1:
ret, _ := core.ReadUInt8(r)
return uint32(ret)
case 2:
ret, _ := core.ReadUint16BE(r)
return uint32(ret)
case 4:
ret, _ := core.ReadUInt32BE(r)
return ret
default:
slog.Debug("ReadInteger")
}
return 0
}
func ReadObjectIdentifier(r io.Reader, oid []byte) bool {
size, _ := ReadLength(r)
if size != 5 {
return false
}
a_oid := []byte{0, 0, 0, 0, 0, 0}
t12, _ := core.ReadByte(r)
a_oid[0] = t12 >> 4
a_oid[1] = t12 & 0x0f
a_oid[2], _ = core.ReadByte(r)
a_oid[3], _ = core.ReadByte(r)
a_oid[4], _ = core.ReadByte(r)
a_oid[5], _ = core.ReadByte(r)
for i := range oid {
if oid[i] != a_oid[i] {
return false
}
}
return true
}
func ReadOctetStream(r io.Reader, s string, min int) bool {
ln, _ := ReadLength(r)
size := int(ln) + min
if size != len(s) {
return false
}
for i := range size {
b, _ := core.ReadByte(r)
if b != s[i] {
return false
}
}
return true
}