Files
aiops/backend/pkg/rdp/fastpath_recv.go
T
LoganandClaude 76ec9387f0 feat(rdp): 新增 RDP 远程桌面/SMB/账单同步能力 + 清理编译产物
主要变更:
- RDP 远程桌面:新增 backend/pkg/rdp(RDP 引擎 + credssp/display/rle/trace)、
  backend/controller/rdp*(连接/凭证/文件/会话/实时)、frontend RDP 组件与 API
- Windows SMB 文件传输:backend/pkg/winsmb
- 云账单同步:backend/pkg/billingsync + billing_sync/aliyun_billing,前端账单详情
- Agent CLI Kubernetes 工具:kubernetes_action/get、pod_execute/logs
- 服务器导入助手:server_import_helpers、ImportCredentialSettings
- 国际化与组件重构(cloud/deploy/monitor/server/system 等多模块)
- 清理编译产物二进制:移除 backend/server、backend/agent/bin/aiops-agent-*
- .gitignore:新增 backend/agent/ 与 .cache/(避免 334MB go-build 缓存误入库)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-04 01:50:36 +08:00

265 lines
6.4 KiB
Go

package rdp
import (
"bytes"
"fmt"
"ezops/pkg/rdp/trace"
)
const (
fastPathOutputCompressionUsed = 0xC0
fastPathFragmentSingle = 0x00
fastPathFragmentLast = 0x10
fastPathFragmentFirst = 0x20
fastPathFragmentNext = 0x30
)
// FastPathReceiver 解析服务端 Fast-Path 更新并产出位图块 // Logan.Li
type FastPathReceiver struct {
mppc *MppcDecompressor
fragments bytes.Buffer
frameIDs []uint32
updateStats map[byte]int
}
func NewFastPathReceiver() *FastPathReceiver {
return &FastPathReceiver{mppc: NewMppcDecompressor(), updateStats: make(map[byte]int)}
}
func (f *FastPathReceiver) Handle(secFlag byte, payload []byte) ([]BitmapRect, error) {
_ = secFlag
r := bytes.NewReader(payload)
var rects []BitmapRect
for r.Len() > 0 {
updateHeader, err := readU8(r)
if err != nil {
return rects, err
}
updateCode := updateHeader & 0x0f
fragmentation := updateHeader & 0x30
compression := updateHeader & 0xC0
f.updateStats[updateCode]++
var compressionFlags byte
if compression == fastPathOutputCompressionUsed {
compressionFlags, err = readU8(r)
if err != nil {
return rects, err
}
}
size, err := readLE16(r)
if err != nil {
return rects, err
}
if f.updateStats[updateCode] <= 8 || f.updateStats[updateCode]%100 == 0 {
trace.Logf("fastpath update code=%s(0x%x) count=%d size=%d frag=0x%02x compression=0x%02x flags=0x%02x", fastPathUpdateName(updateCode), updateCode, f.updateStats[updateCode], size, fragmentation, compression, compressionFlags)
}
block := make([]byte, int(size))
if _, err = r.Read(block); err != nil {
return rects, err
}
if fragmentation != fastPathFragmentSingle {
if fragmentation == fastPathFragmentFirst {
f.fragments.Reset()
}
f.fragments.Write(block)
if fragmentation != fastPathFragmentLast {
continue
}
block = append([]byte(nil), f.fragments.Bytes()...)
}
if compressionFlags != 0 {
decompressed, err := f.mppc.Decompress(compressionFlags, block)
if err != nil {
continue
}
block = decompressed
}
switch updateCode {
case fastPathUpdateBitmap:
bs, err := parseFastPathBitmap(block)
if err != nil {
return rects, err
}
rects = append(rects, bs...)
case fastPathUpdateSurfaceCmds:
bs, frameIDs, err := parseSurfaceCommands(block)
if err != nil {
return rects, err
}
f.frameIDs = append(f.frameIDs, frameIDs...)
rects = append(rects, bs...)
default:
trace.Logf("fastpath skip update code=%s(0x%x) size=%d", fastPathUpdateName(updateCode), updateCode, len(block))
}
}
return rects, nil
}
func fastPathUpdateName(code byte) string {
switch code {
case 0x0:
return "orders"
case fastPathUpdateBitmap:
return "bitmap"
case 0x2:
return "palette"
case 0x3:
return "synchronize"
case fastPathUpdateSurfaceCmds:
return "surface"
case 0x5:
return "ptr_null"
case 0x6:
return "ptr_default"
case 0x8:
return "ptr_position"
case 0x9:
return "color"
case 0x0a:
return "ptr_cached"
case 0x0b:
return "pointer"
case 0x0c:
return "large_pointer"
default:
return "unknown"
}
}
// TakeFrameIDs 取出需要确认的 Surface frame,避免服务端因队列满停止推帧 // Logan.Li
func (f *FastPathReceiver) TakeFrameIDs() []uint32 {
if len(f.frameIDs) == 0 {
return nil
}
out := append([]uint32(nil), f.frameIDs...)
f.frameIDs = f.frameIDs[:0]
return out
}
func (f *FastPathReceiver) HandleSlowPath(payload []byte) ([]BitmapRect, error) {
ind, err := parseMCSDataIndication(payload)
if err != nil {
return nil, err
}
if len(ind.payload) < 6 {
return nil, nil
}
return parseSlowPathBitmap(ind.payload[4:])
}
func parseSurfaceCommands(data []byte) ([]BitmapRect, []uint32, error) {
r := bytes.NewReader(data)
var out []BitmapRect
var frameIDs []uint32
for r.Len() >= 2 {
cmdType, err := readLE16(r)
if err != nil {
return out, frameIDs, err
}
switch cmdType {
case 0x0001, 0x0006:
wb, err := readSurfaceBits(r)
if err != nil {
return out, frameIDs, err
}
if rect, ok := decodeWireBitmap(wb); ok {
out = append(out, rect)
}
case 0x0004:
frameAction, err := readLE16(r)
if err != nil {
return out, frameIDs, err
}
frameID, err := readLE32(r)
if err != nil {
return out, frameIDs, err
}
trace.Logf("surface frame marker action=%d frameID=%d", frameAction, frameID)
if frameAction == 0x0001 {
frameIDs = append(frameIDs, frameID)
}
default:
return out, frameIDs, fmt.Errorf("未知 SurfaceCmd: 0x%04x", cmdType)
}
}
return out, frameIDs, nil
}
func readSurfaceBits(r *bytes.Reader) (wireBitmap, error) {
destLeft, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
destTop, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
destRight, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
destBottom, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
bpp, err := readU8(r)
if err != nil {
return wireBitmap{}, err
}
flags, err := readU8(r)
if err != nil {
return wireBitmap{}, err
}
if _, err = readU8(r); err != nil {
return wireBitmap{}, err
}
codecID, err := readU8(r)
if err != nil {
return wireBitmap{}, err
}
width, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
height, err := readLE16(r)
if err != nil {
return wireBitmap{}, err
}
dataLen, err := readLE32(r)
if err != nil {
return wireBitmap{}, err
}
if flags&0x01 != 0 {
if dataLen < 24 {
return wireBitmap{}, fmt.Errorf("SurfaceBits 扩展头长度非法: %d", dataLen)
}
if _, err = r.Seek(24, 1); err != nil {
return wireBitmap{}, err
}
dataLen -= 24
}
data := make([]byte, int(dataLen))
if _, err = r.Read(data); err != nil {
return wireBitmap{}, err
}
if codecID != 0 {
trace.Logf("surface skip unsupported codec=%d rect=%dx%d@%d,%d data=%d flags=0x%02x", codecID, width, height, destLeft, destTop, dataLen, flags)
return wireBitmap{}, nil
}
trace.Logf("surface bits codec=%d rect=%dx%d@%d,%d data=%d flags=0x%02x", codecID, width, height, destLeft, destTop, dataLen, flags)
return wireBitmap{
DestLeft: destLeft, DestTop: destTop, DestRight: destRight, DestBottom: destBottom,
Width: width, Height: height, BitsPerPixel: uint16(bpp), Flags: uint16(flags) | bitmapNoProcessing,
BitmapDataStream: data,
}, nil
}
func readU8(r *bytes.Reader) (byte, error) {
b, err := r.ReadByte()
return b, err
}