跳到主要内容

proto 录制格式与协议解析

gogetway 把所有捕获的字节流以自定义二进制格式串到一个文件里。proto 包负责"写入"与"读回"。

源码:proto/writeProto.go · proto/readProto.go


1. 二进制格式

MagicHeader("--benbens--\nt") + TimeStamp(8B LE) + Type(1B) + Length(8B LE) + "from...to" + "\n" + Body
长度说明
MagicHeader13B固定 --benbens--\nt,用作切分定界
TimeStamp8B纳秒级 Unix 时间戳,小端
Type1BTypes.ClientType(TCP=1 / HTTP=2 等)
Length8Bbody 长度,小端
FromTo变长from-host:port...to-host:port
\n1B分隔符
BodyLength B原始字节流

转义:body 中若出现 \nt 序列,会被 handleDataBody 替换为 \n\nt(避免与 MagicHeader 冲突),UnMarshal 时通过 removeNewlineAfter 还原。


2. Packet 结构

type Packet struct {
Data []byte // 还原后的载荷
Type Types.ClientType // 与写入时的 ListenType 对应
From string // 源地址
To string // 目标地址
reqTimestamp int64 // 私有,写入时的纳秒时间戳
}

func (p *Packet) Marshal() []byte
func (p *Packet) Timestamp() int64
func NewPacket(data []byte, From, To string, PacketType Types.ClientType) *Packet

注意reqTimestamp 是私有字段,NewPacket 创建出来的 Packet 的 Timestamp() 永远返回 0,只有从 UnMarshal 出来的 Packet 才带真实时间戳。


3. 关键函数

3.1 WriteProto

func WriteProto(src []byte, PacType Types.ClientType, FromTo string) []byte
参数类型说明
src[]byte原始载荷
PacTypeTypes.ClientType包类型
FromTostring形如 "127.0.0.1:54321...127.0.0.1:8080"

返回:已封装好的字节流,可直接写入文件。

3.2 UnMarshal

func UnMarshal(data []byte) (*Packet, error)
参数类型说明
data[]byte单个完整的录制块(由 ReadProtoFromReader 切出)
返回值类型说明
*Packet*Packet解析结果
errerror"packet too short" / "not my packet" / "packet length error"

3.3 ReadProtoFromReader

func ReadProtoFromReader(reader io.Reader) (*bufio.Scanner, error)
参数类型说明
readerio.Reader任意流(文件/网络)
返回值类型说明
*bufio.Scanner*bufio.Scanner每次 Scan() 返回一个完整包的字节(含 MagicHeader),最大单包 64MB
errerror当前实现总是 nil

4. 典型读取用法

file, _ := os.Open("./traffic.log")
defer file.Close()

scanner, _ := proto.ReadProtoFromReader(file)
for scanner.Scan() {
pkt, err := proto.UnMarshal(scanner.Bytes())
if err != nil { continue }
fmt.Printf("[%d] %s -> %s len=%d\n", pkt.Timestamp(), pkt.From, pkt.To, len(pkt.Data))
}

5. Pack 接口

type Pack interface {
Marshal() []byte
Timestamp() int64
Type() Types.ClientType
Data() []byte
From() string
To() string
}

*proto.Packet 部分实现了这个接口(Marshal / Timestamp),其它方法目前通过字段直接访问。 如果你要换一种录制格式(例如 pcapng / JSON line),只要保证读写一对函数即可,其它模块都基于 []byte / *Packet 工作。


6. 上下文 key 速查

主动代理写入时,context.Context 里可用的 key 定义在 getwayServer/consts.go

const (
ListenType = "ListenType" // value: Types.ClientType
FromTo = "FromTo" // value: "from-host:port...to-host:port"
FromIP = "FromIP" // value: "host:port"
ToIP = "ToIP" // value: "host:port"
)

读取示例:

writeFunc := func(data []byte, ctx context.Context) (int, error) {
fromTo, _ := ctx.Value(getwayServer.FromTo).(string)
listenType, _ := ctx.Value(getwayServer.ListenType).(Types.ClientType)
_ = listenType
return n, nil
}

被动镜像写入时也注入这四个 key,所以同一份 WriteFunc 可以同时给主动代理和被动镜像复用。


参考:SimpleTCPServer · TcpPlayer