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
+131
View File
@@ -0,0 +1,131 @@
package core
import (
"encoding/binary"
"io"
)
type ReadBytesComplete func(result []byte, err error)
func StartReadBytes(len int, r io.Reader, cb ReadBytesComplete) {
b := make([]byte, len)
go func() {
_, err := io.ReadFull(r, b)
cb(b, err)
}()
}
func ReadBytes(len int, r io.Reader) ([]byte, error) {
b := make([]byte, len)
length, err := io.ReadFull(r, b)
return b[:length], err
}
func ReadByte(r io.Reader) (byte, error) {
var buf [1]byte
_, err := io.ReadFull(r, buf[:])
return buf[0], err
}
func ReadUInt8(r io.Reader) (uint8, error) {
var buf [1]byte
_, err := io.ReadFull(r, buf[:])
return buf[0], err
}
func ReadUint16LE(r io.Reader) (uint16, error) {
var buf [2]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint16(buf[:]), nil
}
func ReadUint16BE(r io.Reader) (uint16, error) {
var buf [2]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.BigEndian.Uint16(buf[:]), nil
}
func ReadUInt32LE(r io.Reader) (uint32, error) {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.LittleEndian.Uint32(buf[:]), nil
}
func ReadUInt32BE(r io.Reader) (uint32, error) {
var buf [4]byte
_, err := io.ReadFull(r, buf[:])
if err != nil {
return 0, err
}
return binary.BigEndian.Uint32(buf[:]), nil
}
func WriteByte(data byte, w io.Writer) (int, error) {
buf := [1]byte{data}
return w.Write(buf[:])
}
func WriteBytes(data []byte, w io.Writer) (int, error) {
return w.Write(data)
}
func WriteUInt8(data uint8, w io.Writer) (int, error) {
buf := [1]byte{data}
return w.Write(buf[:])
}
func WriteUInt16BE(data uint16, w io.Writer) (int, error) {
var buf [2]byte
binary.BigEndian.PutUint16(buf[:], data)
return w.Write(buf[:])
}
func WriteUInt16LE(data uint16, w io.Writer) (int, error) {
var buf [2]byte
binary.LittleEndian.PutUint16(buf[:], data)
return w.Write(buf[:])
}
func WriteUInt32LE(data uint32, w io.Writer) (int, error) {
var buf [4]byte
binary.LittleEndian.PutUint32(buf[:], data)
return w.Write(buf[:])
}
func WriteUInt32BE(data uint32, w io.Writer) (int, error) {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], data)
return w.Write(buf[:])
}
func PutUint16BE(data uint16) (uint8, uint8) {
return uint8(data >> 8), uint8(data)
}
func Uint16BE(d0, d1 uint8) uint16 {
return uint16(d0)<<8 | uint16(d1)
}
func RGB565ToRGB(data uint16) (r, g, b uint8) {
r = uint8((data & 0xF800) >> 8)
g = uint8((data & 0x07E0) >> 3)
b = uint8((data & 0x001F) << 3)
return
}
func RGB555ToRGB(data uint16) (r, g, b uint8) {
r = uint8((data & 0x7C00) >> 7)
g = uint8((data & 0x03E0) >> 2)
b = uint8((data & 0x001F) << 3)
return
}
+17
View File
@@ -0,0 +1,17 @@
package core
import (
"bytes"
"encoding/hex"
"testing"
)
func TestWriteUInt16LE(t *testing.T) {
buff := &bytes.Buffer{}
WriteUInt32LE(66538, buff)
result := hex.EncodeToString(buff.Bytes())
expected := "ea030100"
if result != expected {
t.Error(result, "not equals to", expected)
}
}
+20
View File
@@ -0,0 +1,20 @@
package core
import (
"encoding/hex"
"log/slog"
)
// Hex wraps a byte slice as a slog.LogValuer that lazily encodes the bytes
// as a hexadecimal string only when the slog handler actually formats it.
//
// Use Hex(buf) instead of hex.EncodeToString(buf) inside slog.Debug calls on
// hot paths: when the logger's level filter discards the record (the common
// case in production), the encode and the per-call string allocation are
// skipped entirely.
type Hex []byte
// LogValue implements slog.LogValuer.
func (h Hex) LogValue() slog.Value {
return slog.StringValue(hex.EncodeToString(h))
}
+205
View File
@@ -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
}
+353
View File
@@ -0,0 +1,353 @@
package core
import (
"bytes"
"math/rand"
"testing"
)
// Token grammar reference (MS-RDPBCGR §3.1.8.4 as implemented by FreeRDP's
// libfreerdp/codec/mppc.c):
//
// 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
// compressedABC is the MPPC-64K encoding of "abc" (three literal tokens;
// bytes < 0x80 are transmitted as 8-bit "0"+7bits tokens).
var compressedABC = []byte{0x61, 0x62, 0x63}
// compressedABCABC is "abc" followed by a copy tuple that repeats it:
//
// "11" copy tuple, "111" offset group 1, offset 000011 (=3),
// length "0" (=3). Bits: 01100001 01100010 01100011 11111000 0110…
// → 0x61 0x62 0x63 0xF8 0x60
//
// Decoded: "abcabc".
var compressedABCABC = []byte{0x61, 0x62, 0x63, 0xF8, 0x60}
func TestMppcDecompressLiterals(t *testing.T) {
d := NewMppcDecompressor()
got, err := d.Decompress(mppcType64K|mppcCompressed, compressedABC)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, []byte("abc")) {
t.Errorf("got %q, want %q", got, "abc")
}
}
func TestMppcDecompressCopyTuple(t *testing.T) {
d := NewMppcDecompressor()
got, err := d.Decompress(mppcType64K|mppcCompressed, compressedABCABC)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, []byte("abcabc")) {
t.Errorf("got %q, want %q", got, "abcabc")
}
}
func TestMppcDecompressFlush(t *testing.T) {
d := NewMppcDecompressor()
// First call: populate history.
_, _ = d.Decompress(mppcType64K|mppcCompressed, compressedABC)
if d.history[0] != 'a' {
t.Fatal("history not populated after first call")
}
// Second call with FLUSH: history must be zeroed, offset restarts at 0.
_, _ = d.Decompress(mppcAtFront|mppcFlushed|mppcCompressed, compressedABC)
if d.history[0] != 'a' || d.history[1] != 'b' || d.history[2] != 'c' {
t.Errorf("unexpected history after flush: %q %q %q",
d.history[0], d.history[1], d.history[2])
}
if d.offset != 3 {
t.Errorf("offset after flush: got %d, want 3", d.offset)
}
}
func TestMppcDecompressReset(t *testing.T) {
d := NewMppcDecompressor()
_, _ = d.Decompress(mppcType64K|mppcCompressed, compressedABC)
if d.offset != 3 {
t.Fatalf("offset after first call: got %d, want 3", d.offset)
}
// RESET (PACKET_AT_FRONT): cursor returns to the front, history contents
// preserved; the fresh tokens overwrite from position 0.
_, _ = d.Decompress(mppcAtFront|mppcType64K|mppcCompressed, compressedABC)
if d.offset != 3 {
t.Fatalf("offset after reset+decompress: got %d, want 3", d.offset)
}
if d.history[0] != 'a' {
t.Errorf("history[0] after reset: got %q", d.history[0])
}
}
func TestMppcDecompressUncompressed(t *testing.T) {
d := NewMppcDecompressor()
plain := []byte("hello")
got, err := d.Decompress(mppcType64K, plain) // no COMPRESSED flag
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, plain) {
t.Errorf("got %q, want %q", got, plain)
}
// Uncompressed segments do NOT advance the shared history (matching
// FreeRDP): only compressed tokens feed it.
if d.offset != 0 {
t.Errorf("offset: got %d, want 0 (history untouched)", d.offset)
}
}
// mppcTestEncoder is a minimal MPPC compressor used to generate reference
// streams. It mirrors the decoder's grammar and history arithmetic exactly.
type mppcTestEncoder struct {
buf bitWriter
hist []byte
mask int
big bool
}
type bitWriter struct {
out []byte
cur byte
nbit uint
}
func (w *bitWriter) writeBit(b int) {
if b != 0 {
w.cur |= 0x80 >> w.nbit
}
w.nbit++
if w.nbit == 8 {
w.out = append(w.out, w.cur)
w.cur = 0
w.nbit = 0
}
}
func (w *bitWriter) writeBits(v, n int) {
for i := n - 1; i >= 0; i-- {
w.writeBit((v >> uint(i)) & 1)
}
}
func (w *bitWriter) flush() []byte {
if w.nbit > 0 {
w.out = append(w.out, w.cur)
w.cur = 0
w.nbit = 0
}
return w.out
}
func newMppcTestEncoder(big bool) *mppcTestEncoder {
mask := 8191
if big {
mask = 65535
}
return &mppcTestEncoder{big: big, mask: mask}
}
func (e *mppcTestEncoder) emitLiteral(b byte) {
if b < 0x80 {
e.buf.writeBits(int(b), 8)
} else {
e.buf.writeBits(2, 2) // "10"
e.buf.writeBits(int(b)&0x7F, 7)
}
e.hist = append(e.hist, b)
}
func (e *mppcTestEncoder) emitCopy(off, length int) {
e.buf.writeBits(3, 2) // "11"
if e.big {
switch {
case off <= 63:
e.buf.writeBits(7, 3) // "111"
e.buf.writeBits(off, 6)
case off <= 319:
e.buf.writeBits(6, 3) // "110"
e.buf.writeBits(off-64, 8)
case off <= 2367:
e.buf.writeBits(2, 2) // "10"
e.buf.writeBits(off-320, 11)
default:
e.buf.writeBit(0)
e.buf.writeBits(off-2368, 16)
}
} else {
switch {
case off <= 63:
e.buf.writeBits(3, 2) // "11"
e.buf.writeBits(off, 6)
case off <= 319:
e.buf.writeBits(2, 2) // "10"
e.buf.writeBits(off-64, 8)
default:
e.buf.writeBit(0)
e.buf.writeBits(off-320, 13)
}
}
// Length: "0" → 3; otherwise n leading 1-bits, a 0 terminator, then n+1
// value bits with the (1<<m) high bit implied (m = n+1).
if length == 3 {
e.buf.writeBit(0)
} else {
m := 0
for (1 << uint(m+1)) <= length {
m++
}
k := m - 1
for i := 0; i < k; i++ {
e.buf.writeBit(1)
}
e.buf.writeBit(0)
e.buf.writeBits(length-(1<<uint(m)), m)
}
// Apply the copy to the encoder's own history view (distance = off).
pos := len(e.hist)
src := (pos - off) & e.mask
for i := 0; i < length; i++ {
e.hist = append(e.hist, e.hist[src])
src = (src + 1) & e.mask
}
}
func TestMppcLiteralRoundTrip(t *testing.T) {
rng := rand.New(rand.NewSource(1))
for _, big := range []bool{false, true} {
enc := newMppcTestEncoder(big)
var want []byte
for i := 0; i < 500; i++ {
b := byte(rng.Intn(256))
enc.emitLiteral(b)
want = append(want, b)
}
// 字面量语法与字典大小无关,两种编码器的字面量流都能被
// 64K 解码器还原。
got, err := NewMppcDecompressor().Decompress(mppcCompressed, enc.buf.flush())
if err != nil {
t.Fatalf("big=%v: %v", big, err)
}
if !bytes.Equal(got, want) {
t.Fatalf("big=%v: mismatch got %d bytes want %d", big, len(got), len(want))
}
}
}
func TestMppcCopyRoundTrip(t *testing.T) {
rng := rand.New(rand.NewSource(2))
// (offset, length) pairs covering every offset group and several length
// groups (3, 4-7, 8-15, 16-31, longer binary forms).
cases := [][2]int{
{5, 3}, {63, 4}, {64, 7}, {100, 3}, {319, 8},
{320, 16}, {1000, 31}, {2367, 9}, {2368, 32},
{5000, 100}, {30000, 300}, {60000, 500},
}
// 解码器固定使用 RDP5 64K 字典(协商决定),8K 语法不再可解。
big := true
enc := newMppcTestEncoder(big)
var want []byte
for _, c := range cases {
off, length := c[0], c[1]
for off > len(enc.hist) {
b := byte(rng.Intn(256))
enc.emitLiteral(b)
want = append(want, b)
}
enc.emitCopy(off, length)
pos := len(want)
src := (pos - off) & enc.mask
for i := 0; i < length; i++ {
want = append(want, want[src])
src = (src + 1) & enc.mask
}
}
got, err := NewMppcDecompressor().Decompress(mppcType64K|mppcCompressed, enc.buf.flush())
if err != nil {
t.Fatalf("%v", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("copy round-trip mismatch (%d vs %d bytes)", len(got), len(want))
}
}
func TestMppcHistoryAcrossSegments(t *testing.T) {
enc := newMppcTestEncoder(true)
d := NewMppcDecompressor()
flags := byte(mppcCompressed) // 无 RESET/AT_FRONT:历史跨段连续
// Segment 1: seed literals.
var want []byte
for i := 0; i < 400; i++ {
b := byte('A' + i%26)
enc.emitLiteral(b)
want = append(want, b)
}
got, err := d.Decompress(flags, enc.buf.flush())
if err != nil || !bytes.Equal(got, want[:len(got)]) {
t.Fatalf("seg1: err=%v len=%d", err, len(got))
}
enc.buf.out = nil
// Segment 2: copy referencing segment 1 content.
enc.emitCopy(200, 50)
got2, err := d.Decompress(flags, enc.buf.flush())
if err != nil {
t.Fatalf("seg2: %v", err)
}
want2 := want[len(want)-200:]
for i := 0; i < 50; i++ {
if got2[i] != want2[i] {
t.Fatalf("seg2 mismatch at %d: got %q want %q", i, got2[i], want2[i])
}
}
}
func TestMppcFlushAndReset(t *testing.T) {
enc := newMppcTestEncoder(true)
d := NewMppcDecompressor()
flags := byte(mppcCompressed) // 无 RESET/AT_FRONT:历史跨段连续
for i := 0; i < 100; i++ {
enc.emitLiteral(byte(i % 256))
}
if _, err := d.Decompress(flags, enc.buf.flush()); err != nil {
t.Fatal(err)
}
if d.offset == 0 {
t.Fatal("history should have advanced")
}
// RESET moves the write cursor to the front without clearing content.
enc2 := newMppcTestEncoder(true)
enc2.emitLiteral('x')
enc2.emitLiteral('y')
got, err := d.Decompress(mppcCompressed|mppcAtFront|mppcType64K, enc2.buf.flush())
if err != nil || string(got) != "xy" {
t.Fatalf("reset: err=%v got=%q", err, got)
}
if d.offset != 2 {
t.Fatalf("reset should restart the cursor, got %d", d.offset)
}
// FLUSH clears the whole dictionary.
got, err = d.Decompress(mppcCompressed|mppcAtFront|mppcFlushed, enc2.buf.flush())
if err != nil || string(got) != "xy" {
t.Fatalf("flush: err=%v got=%q", err, got)
}
}
func TestMppcRejectsGarbage(t *testing.T) {
d := NewMppcDecompressor()
// A long run of 1-bits overflows the LengthOfMatch prefix code.
data := make([]byte, 16)
for i := range data {
data[i] = 0xFF
}
if _, err := d.Decompress(mppcCompressed|mppcType64K, data); err == nil {
t.Fatal("expected error for length code overflow")
}
}
+997
View File
@@ -0,0 +1,997 @@
package core
import (
"fmt"
"log/slog"
"sync"
"unsafe"
)
func CVAL(p *[]uint8) int {
a := int((*p)[0])
*p = (*p)[1:]
return a
}
func CVAL2(p *[]uint8, v *uint16) {
*v = *((*uint16)(unsafe.Pointer(&(*p)[0])))
*p = (*p)[2:]
}
func CVAL3(p *[]uint8, v *[3]uint8) {
(*v)[0] = (*p)[0]
(*v)[1] = (*p)[1]
(*v)[2] = (*p)[2]
*p = (*p)[3:]
}
func REPEAT(f func(), count *int, x *int, width int) {
for *count > 0 && *x < width {
f()
*count--
*x++
}
}
// rleFailLog 记录 RLE 解码失败(限次),此前失败被静默吞掉:
// 半解码的池化缓冲被直接画上画布形成噪块,且无任何日志可查。
func rleFailLog(reason string, width, height, inputLen int, consumed int) {
slog.Warn("bitmap RLE decode failed", "reason", reason,
"w", width, "h", height, "inputLen", inputLen, "consumed", consumed)
}
/* 1 byte bitmap decompress */
func decompress1(output *[]uint8, width, height int, input []uint8, size int) bool {
var (
prevline, line, count int
offset, code int
x int = width
opcode int
lastopcode int8 = -1
insertmix, bicolour, isfillormix bool
mixmask, mask uint8
colour1, colour2 uint8
mix uint8 = 0xff
fom_mask uint8
)
out := *output
for len(input) != 0 {
fom_mask = 0
code = CVAL(&input)
opcode = code >> 4
/* Handle different opcode forms */
switch opcode {
case 0xc, 0xd, 0xe:
opcode -= 6
count = int(code & 0xf)
offset = 16
break
case 0xf:
opcode = code & 0xf
if opcode < 9 {
count = int(CVAL(&input))
count |= int(CVAL(&input) << 8)
} else {
count = 1
if opcode < 0xb {
count = 8
}
}
offset = 0
break
default:
opcode >>= 1
count = int(code & 0x1f)
offset = 32
break
}
/* Handle strange cases for counts */
if offset != 0 {
isfillormix = ((opcode == 2) || (opcode == 7))
if count == 0 {
if isfillormix {
count = int(CVAL(&input)) + 1
} else {
count = int(CVAL(&input) + offset)
}
} else if isfillormix {
count <<= 3
}
}
/* Read preliminary data */
switch opcode {
case 0: /* Fill */
if (lastopcode == int8(opcode)) && !((x == width) && (prevline == 0)) {
insertmix = true
}
break
case 8: /* Bicolour */
colour1 = uint8(CVAL(&input))
colour2 = uint8(CVAL(&input))
break
case 3: /* Colour */
colour2 = uint8(CVAL(&input))
break
case 6: /* SetMix/Mix */
fallthrough
case 7: /* SetMix/FillOrMix */
mix = uint8(CVAL(&input))
opcode -= 5
break
case 9: /* FillOrMix_1 */
mask = 0x03
opcode = 0x02
fom_mask = 3
break
case 0x0a: /* FillOrMix_2 */
mask = 0x05
opcode = 0x02
fom_mask = 5
break
}
lastopcode = int8(opcode)
mixmask = 0
/* Output body */
for count > 0 {
if x >= width {
if height <= 0 {
return false
}
x = 0
height--
prevline = line
line = height * width
}
switch opcode {
case 0: /* Fill */
if insertmix {
if prevline == 0 {
out[x+line] = mix
} else {
out[x+line] = out[prevline+x] ^ mix
}
insertmix = false
count--
x++
}
n := min(count, width-x)
if prevline == 0 {
clear(out[x+line : x+line+n])
} else {
copy(out[x+line:x+line+n], out[prevline+x:prevline+x+n])
}
count -= n
x += n
break
case 1: /* Mix */
n := min(count, width-x)
if prevline == 0 {
seg := out[x+line : x+line+n]
for i := range seg {
seg[i] = mix
}
} else {
src := out[prevline+x : prevline+x+n]
dst := out[x+line : x+line+n]
for i := range dst {
dst[i] = src[i] ^ mix
}
}
count -= n
x += n
break
case 2: /* Fill or Mix */
if prevline == 0 {
for count > 0 && x < width {
mixmask <<= 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[x+line] = mix
} else {
out[x+line] = 0
}
count--
x++
}
} else {
for count > 0 && x < width {
mixmask = mixmask << 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[x+line] = out[prevline+x] ^ mix
} else {
out[x+line] = out[prevline+x]
}
count--
x++
}
}
break
case 3: /* Colour */
n := min(count, width-x)
seg := out[x+line : x+line+n]
for i := range seg {
seg[i] = colour2
}
count -= n
x += n
break
case 4: /* Copy */
for count > 0 && x < width {
n := min(count, width-x)
if len(input) < n {
return false
}
copy(out[x+line:x+line+n], input[:n])
input = input[n:]
count -= n
x += n
}
break
case 8: /* Bicolour */
for count > 0 && x < width {
if bicolour {
out[x+line] = colour2
bicolour = false
} else {
out[x+line] = colour1
bicolour = true
count++
}
count--
x++
}
break
case 0xd: /* White */
n := min(count, width-x)
seg := out[x+line : x+line+n]
for i := range seg {
seg[i] = 0xff
}
count -= n
x += n
break
case 0xe: /* Black */
n := min(count, width-x)
clear(out[x+line : x+line+n])
count -= n
x += n
break
default:
fmt.Printf("bitmap opcode 0x%x\n", opcode)
return false
}
}
}
return true
}
// decompress2Pool reuses the intermediate []uint16 buffer across calls to
// decompress2, avoiding a large per-frame allocation.
var decompress2Pool sync.Pool
/* 2 byte bitmap decompress */
func decompress2(output *[]uint8, width, height int, input []uint8, size int) bool {
needed := width * height
var out []uint16
if v := decompress2Pool.Get(); v != nil {
out = v.([]uint16)
if cap(out) < needed {
out = make([]uint16, needed)
} else {
out = out[:needed]
}
} else {
out = make([]uint16, needed)
}
defer func() { decompress2Pool.Put(out[:cap(out)]) }()
var (
prevline, line, count int
offset, code int
x int = width
opcode int
lastopcode int = -1
insertmix, bicolour, isfillormix bool
mixmask, mask uint8
colour1, colour2 uint16
mix uint16 = 0xffff
fom_mask uint8
)
inputLen0 := len(input)
for len(input) != 0 {
fom_mask = 0
code = CVAL(&input)
opcode = code >> 4
/* Handle different opcode forms */
switch opcode {
case 0xc, 0xd, 0xe:
opcode -= 6
count = code & 0xf
offset = 16
break
case 0xf:
opcode = code & 0xf
if opcode < 9 {
count = CVAL(&input)
count |= CVAL(&input) << 8
} else {
count = 1
if opcode < 0xb {
count = 8
}
}
offset = 0
break
default:
opcode >>= 1
count = code & 0x1f
offset = 32
break
}
/* Handle strange cases for counts */
if offset != 0 {
isfillormix = ((opcode == 2) || (opcode == 7))
if count == 0 {
if isfillormix {
count = CVAL(&input) + 1
} else {
count = CVAL(&input) + offset
}
} else if isfillormix {
count <<= 3
}
}
/* Read preliminary data */
switch opcode {
case 0: /* Fill */
if (lastopcode == opcode) && !((x == width) && (prevline == 0)) {
insertmix = true
}
break
case 8: /* Bicolour */
CVAL2(&input, &colour1)
CVAL2(&input, &colour2)
break
case 3: /* Colour */
CVAL2(&input, &colour2)
break
case 6: /* SetMix/Mix */
fallthrough
case 7: /* SetMix/FillOrMix */
CVAL2(&input, &mix)
opcode -= 5
break
case 9: /* FillOrMix_1 */
mask = 0x03
opcode = 0x02
fom_mask = 3
break
case 0x0a: /* FillOrMix_2 */
mask = 0x05
opcode = 0x02
fom_mask = 5
break
}
lastopcode = opcode
mixmask = 0
/* Output body */
for count > 0 {
if x >= width {
if height <= 0 {
rleFailLog("lines exhausted", width, height, inputLen0, inputLen0-len(input))
return false
}
x = 0
height--
prevline = line
line = height * width
}
switch opcode {
case 0: /* Fill */
if insertmix {
if prevline == 0 {
out[x+line] = mix
} else {
out[x+line] = out[prevline+x] ^ mix
}
insertmix = false
count--
x++
}
n := min(count, width-x)
if prevline == 0 {
clear(out[x+line : x+line+n])
} else {
copy(out[x+line:x+line+n], out[prevline+x:prevline+x+n])
}
count -= n
x += n
break
case 1: /* Mix */
n := min(count, width-x)
if prevline == 0 {
seg := out[x+line : x+line+n]
for i := range seg {
seg[i] = mix
}
} else {
src := out[prevline+x : prevline+x+n]
dst := out[x+line : x+line+n]
for i := range dst {
dst[i] = src[i] ^ mix
}
}
count -= n
x += n
break
case 2: /* Fill or Mix */
if prevline == 0 {
for count > 0 && x < width {
mixmask <<= 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[x+line] = mix
} else {
out[x+line] = 0
}
count--
x++
}
} else {
for count > 0 && x < width {
mixmask = mixmask << 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[x+line] = out[prevline+x] ^ mix
} else {
out[x+line] = out[prevline+x]
}
count--
x++
}
}
break
case 3: /* Colour */
n := min(count, width-x)
seg := out[x+line : x+line+n]
for i := range seg {
seg[i] = colour2
}
count -= n
x += n
break
case 4: /* Copy */
for count > 0 && x < width {
n := min(count, width-x)
if len(input) < n*2 {
rleFailLog("copy input exhausted", width, height, inputLen0, inputLen0-len(input))
return false
}
copy(out[x+line:x+line+n], unsafe.Slice((*uint16)(unsafe.Pointer(&input[0])), n))
input = input[n*2:]
count -= n
x += n
}
break
case 8: /* Bicolour */
for count > 0 && x < width {
if bicolour {
out[x+line] = colour2
bicolour = false
} else {
out[x+line] = colour1
bicolour = true
count++
}
count--
x++
}
break
case 0xd: /* White */
n2 := min(count, width-x)
seg2 := out[x+line : x+line+n2]
for i := range seg2 {
seg2[i] = 0xffff
}
count -= n2
x += n2
break
case 0xe: /* Black */
n3 := min(count, width-x)
clear(out[x+line : x+line+n3])
count -= n3
x += n3
break
default:
rleFailLog(fmt.Sprintf("bad opcode 0x%x", opcode), width, height, inputLen0, inputLen0-len(input))
return false
}
}
}
outBytes := *output
for i, v := range out {
outBytes[i*2] = byte(v >> 8)
outBytes[i*2+1] = byte(v)
}
return true
}
// /* 3 byte bitmap decompress */
func decompress3(output *[]uint8, width, height int, input []uint8, size int) bool {
var (
prevline, line, count int
opcode, offset, code int
x int = width
lastopcode int = -1
insertmix, bicolour, isfillormix bool
mixmask, mask uint8
colour1 = [3]uint8{0, 0, 0}
colour2 = [3]uint8{0, 0, 0}
mix = [3]uint8{0xff, 0xff, 0xff}
fom_mask uint8
)
out := *output
for len(input) != 0 {
fom_mask = 0
code = CVAL(&input)
opcode = code >> 4
/* Handle different opcode forms */
switch opcode {
case 0xc, 0xd, 0xe:
opcode -= 6
count = code & 0xf
offset = 16
break
case 0xf:
opcode = code & 0xf
if opcode < 9 {
count = CVAL(&input)
count |= CVAL(&input) << 8
} else {
count = 1
if opcode < 0xb {
count = 8
}
}
offset = 0
break
default:
opcode >>= 1
count = code & 0x1f
offset = 32
break
}
/* Handle strange cases for counts */
if offset != 0 {
isfillormix = ((opcode == 2) || (opcode == 7))
if count == 0 {
if isfillormix {
count = CVAL(&input) + 1
} else {
count = CVAL(&input) + offset
}
} else if isfillormix {
count <<= 3
}
}
/* Read preliminary data */
switch opcode {
case 0: /* Fill */
if (lastopcode == opcode) && !((x == width) && (prevline == 0)) {
insertmix = true
}
break
case 8: /* Bicolour */
CVAL3(&input, &colour1)
CVAL3(&input, &colour2)
break
case 3: /* Colour */
CVAL3(&input, &colour2)
break
case 6: /* SetMix/Mix */
fallthrough
case 7: /* SetMix/FillOrMix */
CVAL3(&input, &mix)
opcode -= 5
break
case 9: /* FillOrMix_1 */
mask = 0x03
opcode = 0x02
fom_mask = 3
break
case 0x0a: /* FillOrMix_2 */
mask = 0x05
opcode = 0x02
fom_mask = 5
break
}
lastopcode = opcode
mixmask = 0
/* Output body */
for count > 0 {
if x >= width {
if height <= 0 {
return false
}
x = 0
height--
prevline = line
line = height * width * 3
}
switch opcode {
case 0: /* Fill */
if insertmix {
if prevline == 0 {
out[3*x+line] = mix[0]
out[3*x+line+1] = mix[1]
out[3*x+line+2] = mix[2]
} else {
out[3*x+line] = out[prevline+3*x] ^ mix[0]
out[3*x+line+1] = out[prevline+3*x+1] ^ mix[1]
out[3*x+line+2] = out[prevline+3*x+2] ^ mix[2]
}
insertmix = false
count--
x++
}
n := min(count, width-x)
if prevline == 0 {
clear(out[3*x+line : 3*x+line+3*n])
} else {
dstBase := 3*x + line
srcBase := prevline + 3*x
copy(out[dstBase:dstBase+3*n], out[srcBase:srcBase+3*n])
}
count -= n
x += n
break
case 1: /* Mix */
n := min(count, width-x)
dst1 := out[3*x+line : 3*x+line+3*n]
if prevline == 0 {
// Exponential-doubling copy: O(log n) memcpy calls
dst1[0], dst1[1], dst1[2] = mix[0], mix[1], mix[2]
for wrote := 3; wrote < len(dst1); {
wrote += copy(dst1[wrote:], dst1[:wrote])
}
} else {
src1 := out[prevline+3*x : prevline+3*x+3*n]
for i := 0; i+2 < len(dst1); i += 3 {
dst1[i] = src1[i] ^ mix[0]
dst1[i+1] = src1[i+1] ^ mix[1]
dst1[i+2] = src1[i+2] ^ mix[2]
}
}
count -= n
x += n
break
case 2: /* Fill or Mix */
if prevline == 0 {
base := 3*x + line
for count > 0 && x < width {
mixmask = mixmask << 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[base] = mix[0]
out[base+1] = mix[1]
out[base+2] = mix[2]
} else {
out[base] = 0
out[base+1] = 0
out[base+2] = 0
}
base += 3
count--
x++
}
} else {
base := 3*x + line
prev := prevline + 3*x
for count > 0 && x < width {
mixmask = mixmask << 1
if mixmask == 0 {
mask = fom_mask
if fom_mask == 0 {
mask = uint8(CVAL(&input))
mixmask = 1
}
}
if mask&mixmask != 0 {
out[base] = out[prev] ^ mix[0]
out[base+1] = out[prev+1] ^ mix[1]
out[base+2] = out[prev+2] ^ mix[2]
} else {
out[base] = out[prev]
out[base+1] = out[prev+1]
out[base+2] = out[prev+2]
}
base += 3
prev += 3
count--
x++
}
}
break
case 3: /* Colour */
n := min(count, width-x)
seg3 := out[3*x+line : 3*x+line+3*n]
seg3[0], seg3[1], seg3[2] = colour2[0], colour2[1], colour2[2]
for wrote := 3; wrote < len(seg3); {
wrote += copy(seg3[wrote:], seg3[:wrote])
}
count -= n
x += n
break
case 4: /* Copy */
for count > 0 && x < width {
n := min(count, width-x)
if len(input) < n*3 {
return false
}
copy(out[3*x+line:3*x+line+n*3], input[:n*3])
input = input[n*3:]
count -= n
x += n
}
break
case 8: /* Bicolour */
base8 := 3*x + line
for count > 0 && x < width {
if bicolour {
out[base8] = colour2[0]
out[base8+1] = colour2[1]
out[base8+2] = colour2[2]
bicolour = false
} else {
out[base8] = colour1[0]
out[base8+1] = colour1[1]
out[base8+2] = colour1[2]
bicolour = true
count++
}
base8 += 3
count--
x++
}
break
case 0xd: /* White */
n3 := min(count, width-x)
seg3 := out[3*x+line : 3*x+line+3*n3]
for i := range seg3 {
seg3[i] = 0xff
}
count -= n3
x += n3
break
case 0xe: /* Black */
n2 := min(count, width-x)
clear(out[3*x+line : 3*x+line+3*n2])
count -= n2
x += n2
break
default:
fmt.Printf("bitmap opcode 0x%x\n", opcode)
return false
}
}
}
return true
}
/* decompress a colour plane */
func processPlane(in *[]uint8, width, height int, output *[]uint8, j int) int {
var (
indexw int
indexh int
code int
collen int
replen int
color uint8
x uint8
revcode int
lastline int
thisline int
)
ln := len(*in)
out := *output // hoist pointer dereference; writes to out[i] affect the underlying array
lastline = 0
indexh = 0
i := 0
for indexh < height {
thisline = j + (width * height * 4) - ((indexh + 1) * width * 4)
color = 0
indexw = 0
i = thisline
if lastline == 0 {
for indexw < width {
code = CVAL(in)
replen = int(code & 0xf)
collen = int((code >> 4) & 0xf)
revcode = (replen << 4) | collen
if (revcode <= 47) && (revcode >= 16) {
replen = revcode
collen = 0
}
for collen > 0 {
color = uint8(CVAL(in))
out[i] = color
i += 4
indexw++
collen--
}
for replen > 0 {
out[i] = color
i += 4
indexw++
replen--
}
}
} else {
// prevOffset is constant per row: indexw*4+lastline == i+(lastline-thisline)
// because i == thisline+indexw*4. Pre-computing it eliminates a multiply
// per pixel in both inner loops.
prevOffset := lastline - thisline
for indexw < width {
code = CVAL(in)
replen = int(code & 0xf)
collen = int((code >> 4) & 0xf)
revcode = (replen << 4) | collen
if (revcode <= 47) && (revcode >= 16) {
replen = revcode
collen = 0
}
for collen > 0 {
x = uint8(CVAL(in))
if x&1 != 0 {
x = x >> 1
x = x + 1
color = -x
} else {
x = x >> 1
color = x
}
x = out[i+prevOffset] + color
out[i] = x
i += 4
indexw++
collen--
}
for replen > 0 {
x = out[i+prevOffset] + color
out[i] = x
i += 4
indexw++
replen--
}
}
}
indexh++
lastline = thisline
}
return ln - len(*in)
}
/* 4 byte bitmap decompress */
func decompress4(output *[]uint8, width, height int, input []uint8, size int) bool {
var (
code int
onceBytes, total int
)
code = CVAL(&input)
rle := code&0x10 != 0
noAlpha := code&0x20 != 0
if !rle {
return false
}
total = 1
out := *output
if noAlpha {
// No alpha plane in the stream; fill alpha channel with 0xFF.
for i := 3; i < len(out); i += 4 {
out[i] = 0xFF
}
} else {
onceBytes = processPlane(&input, width, height, output, 3)
total += onceBytes
}
onceBytes = processPlane(&input, width, height, output, 2)
total += onceBytes
onceBytes = processPlane(&input, width, height, output, 1)
total += onceBytes
onceBytes = processPlane(&input, width, height, output, 0)
total += onceBytes
return true
}
// DecompressInto decompresses bitmap data into dst, reusing dst if it has
// sufficient capacity (size = width*height*bpp). If dst is nil or too small
// a new slice is allocated. Returns the (re)used output slice.
func DecompressInto(input []uint8, dst []uint8, width, height int, bpp int) ([]uint8, bool) {
size := width * height * bpp
if cap(dst) >= size {
dst = dst[:size]
} else {
dst = make([]uint8, size)
}
ok := false
switch bpp {
case 1:
ok = decompress1(&dst, width, height, input, size)
case 2:
ok = decompress2(&dst, width, height, input, size)
case 3:
ok = decompress3(&dst, width, height, input, size)
case 4:
ok = decompress4(&dst, width, height, input, size)
default:
fmt.Printf("bpp %d\n", bpp)
}
return dst, ok
}
/* main decompress function */
func Decompress(input []uint8, width, height int, bpp int) []uint8 {
out, _ := DecompressInto(input, nil, width, height, bpp)
return out
}
+26
View File
@@ -0,0 +1,26 @@
// rle_test.go
package core
import (
"fmt"
"testing"
)
func BenchmarkDecompress3(b *testing.B) {
input := []byte{
192, 44, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8, 0, 19, 132, 232, 8, 12, 50, 142, 66, 77, 58, 208, 59, 225, 25, 1, 0, 0, 0, 0, 0, 0, 0, 132, 139, 33, 142, 66, 142, 66, 142, 66, 208, 59, 4, 43, 1, 0, 0, 0, 0, 0, 0, 0, 132, 203, 41, 142, 66, 142, 66, 142, 66, 208, 59, 96, 0, 1, 0, 0, 0, 0, 0, 0, 0, 132, 9, 17, 142, 66, 142, 66, 142, 66, 208, 59, 230, 27, 1, 0, 0, 0, 0, 0, 0, 0, 132, 200, 8, 9, 17, 139, 33, 74, 25, 243, 133, 14, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8,
}
dst := make([]uint8, 64*64*3)
b.ResetTimer()
for i := 0; i < b.N; i++ {
DecompressInto(input, dst, 64, 64, 3)
}
}
func TestSum(t *testing.T) {
input := []byte{
192, 44, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8, 0, 19, 132, 232, 8, 12, 50, 142, 66, 77, 58, 208, 59, 225, 25, 1, 0, 0, 0, 0, 0, 0, 0, 132, 139, 33, 142, 66, 142, 66, 142, 66, 208, 59, 4, 43, 1, 0, 0, 0, 0, 0, 0, 0, 132, 203, 41, 142, 66, 142, 66, 142, 66, 208, 59, 96, 0, 1, 0, 0, 0, 0, 0, 0, 0, 132, 9, 17, 142, 66, 142, 66, 142, 66, 208, 59, 230, 27, 1, 0, 0, 0, 0, 0, 0, 0, 132, 200, 8, 9, 17, 139, 33, 74, 25, 243, 133, 14, 200, 8, 132, 200, 8, 200, 8, 200, 8, 200, 8,
}
out := Decompress(input, 64, 64, 3)
fmt.Println(out)
}
+127
View File
@@ -0,0 +1,127 @@
package core
import (
"bufio"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"encoding/asn1"
"errors"
"math/big"
"net"
"time"
)
// readBufSize is the size of the buffered reader used for socket reads.
// RDP packets can be large (bitmap updates, channel data); a 64 KiB buffer
// keeps the number of read(2) syscalls low without wasting memory.
const readBufSize = 65536
// tcpRecvBufSize is the OS-level TCP receive socket buffer size.
// The default on most systems (~87 KiB on Linux, ~128 KiB on macOS) is too
// small for high-resolution RDP sessions where the server can burst several
// MiB of bitmap/H.264 data per frame. 512 KiB allows the kernel to buffer
// more in-flight data, reducing stalls when the application goroutine is
// briefly busy decoding a previous frame.
const tcpRecvBufSize = 512 * 1024
type SocketLayer struct {
conn net.Conn
tlsConn *tls.Conn
reader *bufio.Reader // buffers reads regardless of TLS state
serverName string
// certVerifier 非空时在 TLS 握手完成后以服务器叶子证书的 SHA-256 指纹调用,
// 返回错误则中断连接(TOFU 指纹校验用)
certVerifier func(sha256Fp []byte) error
}
func NewSocketLayer(conn net.Conn, serverName string) *SocketLayer {
// Disable Nagle's algorithm so small DVC responses are sent immediately.
if tc, ok := conn.(*net.TCPConn); ok {
tc.SetNoDelay(true)
// Increase the OS receive buffer so the kernel can absorb large bitmap
// or H.264 bursts without dropping bytes while the decoder is busy.
// SetReadBuffer is a best-effort hint; ignore errors (e.g. restricted
// by the OS cap in /proc/sys/net/core/rmem_max on Linux).
_ = tc.SetReadBuffer(tcpRecvBufSize)
}
l := &SocketLayer{
conn: conn,
tlsConn: nil,
serverName: serverName,
}
l.reader = bufio.NewReaderSize(conn, readBufSize)
return l
}
func (s *SocketLayer) SetDeadline(t time.Time) error {
return s.conn.SetDeadline(t)
}
func (s *SocketLayer) Read(b []byte) (n int, err error) {
return s.reader.Read(b)
}
func (s *SocketLayer) Write(b []byte) (n int, err error) {
if s.tlsConn != nil {
return s.tlsConn.Write(b)
}
return s.conn.Write(b)
}
func (s *SocketLayer) Close() error {
if s.tlsConn != nil {
s.tlsConn.Close() // best-effort; always close the underlying TCP socket
}
return s.conn.Close()
}
// SetCertVerifier 注册服务器证书指纹校验回调(须在 StartTLS 前调用)
func (s *SocketLayer) SetCertVerifier(fn func(sha256Fp []byte) error) {
s.certVerifier = fn
}
func (s *SocketLayer) StartTLS() error {
config := &tls.Config{
InsecureSkipVerify: true,
ServerName: s.serverName,
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
// MaxVersion: tls.VersionTLS13,
}
tlsConn := tls.Client(s.conn, config)
if err := tlsConn.Handshake(); err != nil {
return err
}
// RDP 服务器普遍使用自签证书,链校验关闭;改为 TOFU 指纹校验:
// 应用层比对叶子证书 SHA-256,不匹配(如中间人)则拒绝继续
if s.certVerifier != nil {
certs := tlsConn.ConnectionState().PeerCertificates
if len(certs) > 0 {
summary := sha256.Sum256(certs[0].Raw)
if err := s.certVerifier(summary[:]); err != nil {
s.tlsConn = tlsConn
return err
}
}
}
s.tlsConn = tlsConn
// Reset the buffered reader to read from the TLS connection.
// Reset discards any unconsumed buffered bytes from the plain-text phase,
// which is correct because the TLS handshake has already consumed them.
s.reader.Reset(tlsConn)
return nil
}
type PublicKey struct {
N *big.Int `asn1:"explicit,tag:0"` // modulus
E int `asn1:"explicit,tag:1"` // public exponent
}
func (s *SocketLayer) TlsPubKey() ([]byte, error) {
if s.tlsConn == nil {
return nil, errors.New("TLS conn does not exist")
}
pub := s.tlsConn.ConnectionState().PeerCertificates[0].PublicKey.(*rsa.PublicKey)
return asn1.Marshal(*pub)
}
+26
View File
@@ -0,0 +1,26 @@
package core
import "git.zeroonesoft.cn/golib/rdplib/emission"
type Transport interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
On(event, listener any) *emission.Emitter
Once(event, listener any) *emission.Emitter
Off(event, listener any) *emission.Emitter
Emit(event any, arguments ...any) *emission.Emitter
}
type FastPathListener interface {
RecvFastPath(secFlag byte, s []byte)
}
type FastPathSender interface {
SendFastPath(secFlag byte, s []byte) (int, error)
}
type ChannelSender interface {
SendToChannel(channel string, s []byte) (int, error)
}
+53
View File
@@ -0,0 +1,53 @@
package core
import (
"crypto/rand"
"encoding/binary"
"unicode/utf16"
)
func Reverse(s []byte) []byte {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
func Random(n int) []byte {
const alpha = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var bytes = make([]byte, n)
rand.Read(bytes)
for i, b := range bytes {
bytes[i] = alpha[b%byte(len(alpha))]
}
return bytes
}
func UTF16ToLittleEndianBytes(u []uint16) []byte {
b := make([]byte, 2*len(u))
for index, value := range u {
binary.LittleEndian.PutUint16(b[index*2:], value)
}
return b
}
func LittleEndianBytesToUTF16(u []byte) []uint16 {
b := make([]uint16, len(u)/2)
for i := range b {
b[i] = binary.LittleEndian.Uint16(u[i*2:])
}
return b
}
// s.encode('utf-16le')
func UnicodeEncode(p string) []byte {
return UTF16ToLittleEndianBytes(utf16.Encode([]rune(p)))
}
func UnicodeDecode(p []byte) string {
return string(utf16.Decode(LittleEndianBytesToUTF16(p)))
}
func BytesToUint64(b []byte) uint64 {
return binary.LittleEndian.Uint64(b)
}