Files
nezha_domains/integration/agentcompat/internal/scenario/legacy_fm_protocol.go
T
2026-07-20 04:51:20 +00:00

116 lines
3.0 KiB
Go

//go:build linux
package scenario
import (
"bytes"
"encoding/binary"
"errors"
"github.com/nezhahq/nezha/integration/agentcompat/internal/fixture"
)
var (
errLegacyFMInvalidFrame = errors.New("legacy FM invalid frame")
errLegacyFMUnexpected = errors.New("legacy FM unexpected frame")
)
type legacyFMRemoteError struct{}
func (legacyFMRemoteError) Error() string { return "legacy FM agent error" }
var errLegacyFMRemote = legacyFMRemoteError{}
const (
legacyFMListOp byte = 0x00
legacyFMDownloadOp byte = 0x01
legacyFMUploadOp byte = 0x02
)
type legacyFMEntry struct {
Name string
Dir bool
}
type legacyFMList struct {
Path string
Entries []legacyFMEntry
}
type legacyFMDownloadHeader struct {
Size uint64
}
func buildLegacyFMList(path fixture.AgentPath) []byte {
return append([]byte{legacyFMListOp}, []byte(path.String())...)
}
func buildLegacyFMUpload(path fixture.AgentPath, size uint64) []byte {
frame := make([]byte, 1+8+len(path.String()))
frame[0] = legacyFMUploadOp
binary.BigEndian.PutUint64(frame[1:9], size)
copy(frame[9:], path.String())
return frame
}
func buildLegacyFMDownload(path fixture.AgentPath) []byte {
return append([]byte{legacyFMDownloadOp}, []byte(path.String())...)
}
func parseLegacyFMList(frame []byte) (legacyFMList, error) {
if message, ok := parseLegacyFMError(frame); ok {
return legacyFMList{}, message
}
if len(frame) < 8 || !bytes.Equal(frame[:4], []byte("NZFN")) {
return legacyFMList{}, errLegacyFMInvalidFrame
}
pathSize := binary.BigEndian.Uint32(frame[4:8])
if pathSize == 0 || uint64(pathSize)+8 > uint64(len(frame)) {
return legacyFMList{}, errLegacyFMInvalidFrame
}
pathEnd := 8 + int(pathSize)
result := legacyFMList{Path: string(frame[8:pathEnd])}
for cursor := pathEnd; cursor < len(frame); {
if len(frame)-cursor < 2 {
return legacyFMList{}, errLegacyFMInvalidFrame
}
entrySize := int(frame[cursor+1])
if entrySize == 0 || cursor+2+entrySize > len(frame) || frame[cursor] > 1 {
return legacyFMList{}, errLegacyFMInvalidFrame
}
result.Entries = append(result.Entries, legacyFMEntry{
Name: string(frame[cursor+2 : cursor+2+entrySize]),
Dir: frame[cursor] == 1,
})
cursor += 2 + entrySize
}
return result, nil
}
func parseLegacyFMDownload(frame []byte) (legacyFMDownloadHeader, error) {
if message, ok := parseLegacyFMError(frame); ok {
return legacyFMDownloadHeader{}, message
}
if len(frame) != 12 || !bytes.Equal(frame[:4], []byte("NZTD")) {
return legacyFMDownloadHeader{}, errLegacyFMInvalidFrame
}
return legacyFMDownloadHeader{Size: binary.BigEndian.Uint64(frame[4:12])}, nil
}
func parseLegacyFMError(frame []byte) (error, bool) {
if len(frame) < 4 || !bytes.Equal(frame[:4], []byte("NERR")) {
return nil, false
}
return errLegacyFMRemote, true
}
func requireLegacyFMMarker(frame []byte, marker string) error {
if message, ok := parseLegacyFMError(frame); ok {
return message
}
if !bytes.Equal(frame, []byte(marker)) {
return errLegacyFMUnexpected
}
return nil
}