init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib
This commit is contained in:
+205
@@ -0,0 +1,205 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MppcDecompressor maintains per-connection state for RDP bulk data
|
||||
// decompression (MS-RDPBCGR §3.1.8.4, RFC 2118 MPPC token grammar). A single
|
||||
// instance is shared between the fast-path and slow-path receivers of one RDP
|
||||
// connection. The token grammar follows the MS-RDPBCGR pseudo-code as
|
||||
// implemented in FreeRDP's libfreerdp/codec/mppc.c (rdesktop's variant uses a
|
||||
// different literal/length encoding that does not interop with Win10 RDP5
|
||||
// streams).
|
||||
//
|
||||
// Token grammar (bit-level, MSB first):
|
||||
//
|
||||
// literal 0x00-0x7F : "0" + 7 bits (8-bit token)
|
||||
// literal 0x80-0xFF : "10" + 7 bits (9-bit token)
|
||||
// copy tuple : "11" + offset prefix + length prefix
|
||||
//
|
||||
// Offset prefix (RDP5, compressionType nibble 0x1, 64K dictionary):
|
||||
//
|
||||
// "11111" + 6 bits → offset 0-63
|
||||
// "11110" + 8 bits → offset 64-319
|
||||
// "1110" + 11 bits → offset 320-2367
|
||||
// "110" + 16 bits → offset 2368-67903
|
||||
//
|
||||
// (RDP4 / 8K dictionary: "1111"+6, "1110"+8, "110"+13.)
|
||||
//
|
||||
// Length prefix ("0" → 3; otherwise n 1-bits, a 0 terminator, then n+1 value
|
||||
// bits with the (1<<m) high bit implied, m = n+1; RDP5 allows m up to 15).
|
||||
//
|
||||
// History: uncompressed segments do NOT touch the shared history; FLUSH
|
||||
// (0x80) zeroes it, RESET/AT_FRONT (0x40) rewinds the write cursor to 0.
|
||||
type MppcDecompressor struct {
|
||||
history [mppcHistorySize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
const mppcHistorySize = 65536
|
||||
|
||||
// mppc flag bits(fast-path compressionFlags 字节,MS-RDPBCGR 2.2.9.1.1.4.1
|
||||
// + FreeRDP bulk.c):
|
||||
//
|
||||
// 低半字节 0x01 – 压缩类型选择:1 = RDP5(64K 字典)
|
||||
// 0x20 PACKET_COMPRESSED – 块为 MPPC 压缩数据
|
||||
// 0x40 PACKET_AT_FRONT – 解压数据写到历史前端(游标归零)
|
||||
// 0x80 PACKET_FLUSHED – 清零历史,游标归零
|
||||
//
|
||||
// 注意:FreeRDP mppc.c 不处理 0x01 位(它只是类型选择),本解码器固定
|
||||
// 使用 64K 字典,因此该位可忽略。
|
||||
const (
|
||||
mppcType64K = 0x01
|
||||
mppcCompressed = 0x20
|
||||
mppcAtFront = 0x40
|
||||
mppcFlushed = 0x80
|
||||
)
|
||||
|
||||
func NewMppcDecompressor() *MppcDecompressor {
|
||||
return &MppcDecompressor{}
|
||||
}
|
||||
|
||||
// Decompress processes one MPPC segment.
|
||||
//
|
||||
// flags is the compressionFlags byte (fast-path) or the slow-path bulk flags;
|
||||
// it carries the PACKET_* bits. Segments without PACKET_COMPRESSED are
|
||||
// returned unchanged and do not affect the history.
|
||||
func (d *MppcDecompressor) Decompress(flags byte, data []byte) ([]byte, error) {
|
||||
if flags&mppcFlushed != 0 {
|
||||
d.history = [mppcHistorySize]byte{}
|
||||
d.offset = 0
|
||||
}
|
||||
if flags&mppcAtFront != 0 {
|
||||
d.offset = 0
|
||||
}
|
||||
|
||||
if flags&mppcCompressed == 0 {
|
||||
out := make([]byte, len(data))
|
||||
copy(out, data)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RDP5 64K 字典(协商固定,见常量注释)。
|
||||
const mask = 65535
|
||||
start := d.offset
|
||||
br := newMppcBitReader(data)
|
||||
|
||||
// abort 与 FreeRDP mppc.c 失败路径语义一致:HistoryPtr 只在成功返回时
|
||||
// 提交,解码中途失败必须回滚写游标,否则后续分段的 match 引用的
|
||||
// 历史从此错位,整条流持续产出垃圾。
|
||||
abort := func(msg string) ([]byte, error) {
|
||||
d.offset = start
|
||||
return nil, errors.New("mppc: " + msg)
|
||||
}
|
||||
|
||||
for br.bitsLeft >= 8 {
|
||||
if br.readBit() == 0 {
|
||||
// Literal 0x00-0x7F ("0" + 7 bits).
|
||||
if d.offset >= mppcHistorySize {
|
||||
return abort("history full")
|
||||
}
|
||||
d.history[d.offset] = byte(br.readBits(7))
|
||||
d.offset++
|
||||
continue
|
||||
}
|
||||
if br.readBit() == 0 {
|
||||
// Literal 0x80-0xFF ("10" + 7 bits, 9-bit token).
|
||||
if d.offset >= mppcHistorySize {
|
||||
return abort("history full")
|
||||
}
|
||||
d.history[d.offset] = byte(0x80 | br.readBits(7))
|
||||
d.offset++
|
||||
continue
|
||||
}
|
||||
|
||||
// Copy tuple: decode CopyOffset (distance back from the write
|
||||
// cursor, masked into the dictionary)。前缀位必须逐位惰性读取,
|
||||
// 不能在 switch 初始化里预先消耗。
|
||||
var copyOffset int
|
||||
if br.readBit() == 0 {
|
||||
copyOffset = br.readBits(16) + 2368
|
||||
} else if br.readBit() == 0 {
|
||||
copyOffset = br.readBits(11) + 320
|
||||
} else if br.readBit() == 0 {
|
||||
copyOffset = br.readBits(8) + 64
|
||||
} else {
|
||||
copyOffset = br.readBits(6)
|
||||
}
|
||||
|
||||
// Decode LengthOfMatch: n leading 1-bits + 0 terminator, then n+1
|
||||
// value bits; length = (1<<(n+1)) | bits. "0" alone means 3.
|
||||
n := 0
|
||||
for br.readBit() == 1 {
|
||||
n++
|
||||
const maxBits = 15
|
||||
if n > maxBits-1 {
|
||||
return abort("length code overflow")
|
||||
}
|
||||
}
|
||||
var copyLength int
|
||||
if n == 0 {
|
||||
copyLength = 3
|
||||
} else {
|
||||
m := n + 1
|
||||
copyLength = (1 << uint(m)) | br.readBits(m)
|
||||
}
|
||||
|
||||
if d.offset+copyLength > mppcHistorySize {
|
||||
return abort(fmt.Sprintf("copy overflows history (%d+%d)", d.offset, copyLength))
|
||||
}
|
||||
src := (d.offset - copyOffset) & mask
|
||||
for i := 0; i < copyLength; i++ {
|
||||
d.history[d.offset] = d.history[src]
|
||||
d.offset++
|
||||
src = (src + 1) & mask
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]byte, d.offset-start)
|
||||
copy(out, d.history[start:d.offset])
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// mppcBitReader reads bits MSB-first from a byte slice. Reading past the
|
||||
// end yields zero bits (matching FreeRDP's padded bit stream behaviour).
|
||||
type mppcBitReader struct {
|
||||
data []byte
|
||||
byteIdx int
|
||||
mask byte // bit mask within current byte; starts at 0x80
|
||||
bitsLeft int // total bits remaining
|
||||
}
|
||||
|
||||
func newMppcBitReader(data []byte) *mppcBitReader {
|
||||
return &mppcBitReader{
|
||||
data: data,
|
||||
mask: 0x80,
|
||||
bitsLeft: len(data) * 8,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mppcBitReader) readBit() int {
|
||||
if r.bitsLeft <= 0 {
|
||||
return 0
|
||||
}
|
||||
r.bitsLeft--
|
||||
var bit int
|
||||
if r.data[r.byteIdx]&r.mask != 0 {
|
||||
bit = 1
|
||||
}
|
||||
r.mask >>= 1
|
||||
if r.mask == 0 {
|
||||
r.mask = 0x80
|
||||
r.byteIdx++
|
||||
}
|
||||
return bit
|
||||
}
|
||||
|
||||
func (r *mppcBitReader) readBits(n int) int {
|
||||
result := 0
|
||||
for i := 0; i < n; i++ {
|
||||
result = (result << 1) | r.readBit()
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user