From 900665ebc9800cb59730f25fd87e72a3ad8b4d10 Mon Sep 17 00:00:00 2001 From: CFC4N Date: Tue, 28 Jun 2022 22:18:05 +0800 Subject: [PATCH 01/17] New feature: add payload parser. Signed-off-by: CFC4N --- pkg/event_processor/iworker.go | 68 +++++++++++++++++++++++++++++++ pkg/event_processor/processor.go | 69 ++++++++++++++++++++++++++++++++ user/event_openssl.go | 4 +- user/ievent.go | 1 + 4 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 pkg/event_processor/iworker.go create mode 100644 pkg/event_processor/processor.go diff --git a/pkg/event_processor/iworker.go b/pkg/event_processor/iworker.go new file mode 100644 index 000000000..4600b5d18 --- /dev/null +++ b/pkg/event_processor/iworker.go @@ -0,0 +1,68 @@ +package event_processor + +import ( + "ecapture/user" + "time" +) + +type IWorker interface { + + // 定时器1 ,定时判断没有后续包,则解析输出 + + // 定时器2, 定时判断没后续包,则通知上层销毁自己 + + // 收包 + Write(event user.IEventStruct) error +} + +type PROCESS_STATUS uint8 +type PACKET_TYPE uint8 + +const ( + PROCESS_STATE_ING PROCESS_STATUS = iota + PROCESS_STATE_DONE +) + +const ( + PACKET_TYPE_UNKNOW PACKET_TYPE = iota + PACKET_TYPE_GZIP + PACKET_TYPE_WEB_SOCKET +) + +type eventWorker struct { + incoming []user.IEventStruct + status PROCESS_STATUS + packetType PACKET_TYPE + ticker time.Ticker +} + +func (this *eventWorker) Write(event user.IEventStruct) { + this.incoming = append(this.incoming, event) + if this.status == PROCESS_STATE_ING { + // 已经确定包类型,比如确定为gzip等 + + // TODO 解包,输出 + + // 重置状态 + + // 清空数组 + + } else { + // 识别包类型 + + } +} + +func (this *eventWorker) guessPacket() { + +} + +func (this *eventWorker) Run() { + for { + select { + case _ = <-this.ticker.C: + // 输出包 + } + } + +} diff --git a/pkg/event_processor/processor.go b/pkg/event_processor/processor.go new file mode 100644 index 000000000..2cb0e41c7 --- /dev/null +++ b/pkg/event_processor/processor.go @@ -0,0 +1,69 @@ +package event_processor + +import ( + "ecapture/user" +) + +const ( + MAX_INCOMING_CHAN_LEN = 1024 + MAX_PARSER_QUEUE_LEN = 1024 +) + +type EventProcessor struct { + // 收包,来自调用者发来的新事件 + incoming chan user.IEventStruct + + // key为 PID+UID+COMMON等确定唯一的信息 + workerQueue map[string]IWorker +} + +func (this *EventProcessor) init() { + + this.incoming = make(chan user.IEventStruct, MAX_INCOMING_CHAN_LEN) + this.workerQueue = make(map[string]IWorker, MAX_PARSER_QUEUE_LEN) +} + +func (this *EventProcessor) Serve() { + for { + select { + case event := <-this.incoming: + this.dispatch(event) + + } + } +} + +func (this *EventProcessor) dispatch(event user.IEventStruct) { + var uuid string = event.GetUUID() + found, eWorker := this.getWorkerByUUID(uuid) + if !found { + // TODO ADD a new eventWorker into queue + } + + err := eWorker.Write(event) + if err != nil { + //... + } +} + +func (this *EventProcessor) Incoming() chan user.IEventStruct { + return this.incoming +} + +func (this EventProcessor) getWorkerByUUID(uuid string) (bool, IWorker) { + var eWorker IWorker + var found bool + eWorker, found = this.workerQueue[uuid] + if !found { + return false, eWorker + } + return true, eWorker +} + +// Write event +func (this *EventProcessor) Write(event user.IEventStruct) { + select { + case this.incoming <- event: + return + } +} diff --git a/user/event_openssl.go b/user/event_openssl.go index 6da924aab..2a2d51023 100644 --- a/user/event_openssl.go +++ b/user/event_openssl.go @@ -1,6 +1,6 @@ /* -Copyright © 2022 CFC4N - + Copyright © 2022 CFC4N + WebSite: https://www.cnxct.com */ package user diff --git a/user/ievent.go b/user/ievent.go index 3ce396bc1..4cff0ea3e 100644 --- a/user/ievent.go +++ b/user/ievent.go @@ -18,4 +18,5 @@ type IEventStruct interface { Module() IModule SetModule(IModule) EventType() EVENT_TYPE + GetUUID() string } From 2a4214932d335e3e21f22f0a90b0847c289e8eb6 Mon Sep 17 00:00:00 2001 From: CFC4N Date: Fri, 1 Jul 2022 22:04:33 +0800 Subject: [PATCH 02/17] add payload parser. Signed-off-by: CFC4N --- go.mod | 3 + go.sum | 28 ++++++++ pkg/event_processor/iworker.go | 112 ++++++++++++++++++++++++------- pkg/event_processor/processor.go | 56 ++++++++++++++-- user/common.go | 11 +++ user/event_bash.go | 12 ++++ user/event_gnutls.go | 13 ++++ user/event_gossl.go | 12 ++++ user/event_mysqld.go | 12 ++++ user/event_nspr.go | 12 ++++ user/event_openssl.go | 34 ++++++++-- user/event_postgres.go | 12 ++++ user/ievent.go | 2 + 13 files changed, 282 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index f5b80b544..976b21f34 100644 --- a/go.mod +++ b/go.mod @@ -25,5 +25,8 @@ require ( github.com/stretchr/testify v1.7.0 // indirect github.com/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect ) diff --git a/go.sum b/go.sum index 265a2896f..ced8d2c8b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao= github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= @@ -69,6 +70,7 @@ github.com/mdlayher/netlink v1.4.1 h1:I154BCU+mKlIf7BgcAJB2r7QjveNPty6uNY1g9ChVf github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00 h1:qEtkL8n1DAHpi5/AOgAckwGQUlMe4+jhL/GMt+GKIks= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -92,10 +94,24 @@ github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -105,8 +121,11 @@ golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -126,7 +145,9 @@ golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= @@ -136,12 +157,19 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/event_processor/iworker.go b/pkg/event_processor/iworker.go index 4600b5d18..f48c4bf03 100644 --- a/pkg/event_processor/iworker.go +++ b/pkg/event_processor/iworker.go @@ -2,6 +2,8 @@ package event_processor import ( "ecapture/user" + "go.uber.org/zap" + "log" "time" ) @@ -13,48 +15,93 @@ type IWorker interface { // 收包 Write(event user.IEventStruct) error + GetUUID() string } -type PROCESS_STATUS uint8 -type PACKET_TYPE uint8 - const ( - PROCESS_STATE_ING PROCESS_STATUS = iota - PROCESS_STATE_DONE -) - -const ( - PACKET_TYPE_UNKNOW PACKET_TYPE = iota - PACKET_TYPE_GZIP - PACKET_TYPE_WEB_SOCKET + MAX_TICKER_COUNT = 5 // 定时器超时时间 + MAX_CHAN_LEN = 16 // 包队列长度 + //MAX_EVENT_LEN = 16 // 事件数组长度 ) type eventWorker struct { - incoming []user.IEventStruct - status PROCESS_STATUS - packetType PACKET_TYPE - ticker time.Ticker + incoming chan user.IEventStruct + //events []user.IEventStruct + status PROCESS_STATUS + packetType PACKET_TYPE + ticker *time.Ticker + tickerCount uint8 + UUID string + processor *EventProcessor + parser IParser } -func (this *eventWorker) Write(event user.IEventStruct) { - this.incoming = append(this.incoming, event) - if this.status == PROCESS_STATE_ING { - // 已经确定包类型,比如确定为gzip等 +func NewEventWorker(uuid string, processor *EventProcessor) IWorker { + eWorker := &eventWorker{} + eWorker.init(uuid, processor) + go func() { + eWorker.Run() + }() + return eWorker +} - // TODO 解包,输出 +func (this *eventWorker) init(uuid string, processor *EventProcessor) { + this.ticker = time.NewTicker(time.Second * 1) + this.incoming = make(chan user.IEventStruct, MAX_CHAN_LEN) + this.UUID = uuid + this.processor = processor +} - // 重置状态 +func (this *eventWorker) GetUUID() string { + return this.UUID +} - // 清空数组 +func (this *eventWorker) Write(event user.IEventStruct) error { + this.incoming <- event + return nil +} - } else { - // 识别包类型 +// 输出包内容 +func (this *eventWorker) Display() { + if this.parser == nil || !this.parser.IsDone() { + return + } + if this.parser.ParserType() != PARSER_TYPE_HTTP_REQUEST { + //TODO 临时i调试 + return } + log.Println("eventWorker:", this.UUID, "display") + + // 输出包内容 + b := this.parser.Display() + this.processor.GetLogger().Info("eventWorker:display packet", zap.String("uuid", this.UUID), zap.Int("payload", len(b))) + // 重置状态 + this.parser.Reset() + + // 设定状态、重置包类型 + this.status = PROCESS_STATE_DONE + this.packetType = PACKET_TYPE_NULL + } -func (this *eventWorker) guessPacket() { +// 解析类型,输出 +func (this *eventWorker) parserEvent(event user.IEventStruct) { + if this.status == PROCESS_STATE_INIT { + // 识别包类型 + parser := NewParser(event.Payload()) + this.parser = parser + } + _, err := this.parser.Write(event.Payload()[:event.PayloadLen()]) + if err != nil { + this.processor.GetLogger().Fatal("eventWorker: detect packet type error:", zap.String("uuid", this.UUID), zap.Error(err)) + } + //this.processor.GetLogger().Info("eventWorker:detect packet type", zap.String("uuid", this.UUID), zap.Uint8("type", uint8(this.parser.ParserType())), zap.String("IParser Name", this.parser.Name())) + // 是否接收完成,能否输出 + if this.parser.IsDone() { + this.Display() + } } func (this *eventWorker) Run() { @@ -62,7 +109,22 @@ func (this *eventWorker) Run() { select { case _ = <-this.ticker.C: // 输出包 + if this.tickerCount > MAX_TICKER_COUNT { + this.Close() + return + } + this.tickerCount++ + case event := <-this.incoming: + // reset tickerCount + this.tickerCount = 0 + this.parserEvent(event) } } } + +func (this *eventWorker) Close() { + this.Display() + this.tickerCount = 0 + this.processor.delWorkerByUUID(this) +} diff --git a/pkg/event_processor/processor.go b/pkg/event_processor/processor.go index 2cb0e41c7..c2a48eff3 100644 --- a/pkg/event_processor/processor.go +++ b/pkg/event_processor/processor.go @@ -2,6 +2,9 @@ package event_processor import ( "ecapture/user" + "fmt" + "go.uber.org/zap" + "sync" ) const ( @@ -10,25 +13,31 @@ const ( ) type EventProcessor struct { + sync.Mutex // 收包,来自调用者发来的新事件 incoming chan user.IEventStruct // key为 PID+UID+COMMON等确定唯一的信息 workerQueue map[string]IWorker + + logger *zap.Logger } -func (this *EventProcessor) init() { +func (this *EventProcessor) GetLogger() *zap.Logger { + return this.logger +} +func (this *EventProcessor) init() { this.incoming = make(chan user.IEventStruct, MAX_INCOMING_CHAN_LEN) this.workerQueue = make(map[string]IWorker, MAX_PARSER_QUEUE_LEN) } +// Write event 处理器读取事件 func (this *EventProcessor) Serve() { for { select { case event := <-this.incoming: this.dispatch(event) - } } } @@ -37,7 +46,9 @@ func (this *EventProcessor) dispatch(event user.IEventStruct) { var uuid string = event.GetUUID() found, eWorker := this.getWorkerByUUID(uuid) if !found { - // TODO ADD a new eventWorker into queue + // ADD a new eventWorker into queue + eWorker = NewEventWorker(event.GetUUID(), this) + this.addWorkerByUUID(eWorker) } err := eWorker.Write(event) @@ -46,11 +57,13 @@ func (this *EventProcessor) dispatch(event user.IEventStruct) { } } -func (this *EventProcessor) Incoming() chan user.IEventStruct { - return this.incoming -} +//func (this *EventProcessor) Incoming() chan user.IEventStruct { +// return this.incoming +//} -func (this EventProcessor) getWorkerByUUID(uuid string) (bool, IWorker) { +func (this *EventProcessor) getWorkerByUUID(uuid string) (bool, IWorker) { + this.Lock() + defer this.Unlock() var eWorker IWorker var found bool eWorker, found = this.workerQueue[uuid] @@ -60,10 +73,39 @@ func (this EventProcessor) getWorkerByUUID(uuid string) (bool, IWorker) { return true, eWorker } +func (this *EventProcessor) addWorkerByUUID(worker IWorker) { + this.Lock() + defer this.Unlock() + this.workerQueue[worker.GetUUID()] = worker +} + +// 每个worker调用该方法,从处理器中删除自己 +func (this *EventProcessor) delWorkerByUUID(worker IWorker) { + this.Lock() + defer this.Unlock() + delete(this.workerQueue, worker.GetUUID()) +} + // Write event +// 外部调用者调用该方法 func (this *EventProcessor) Write(event user.IEventStruct) { select { case this.incoming <- event: return } } + +func (this *EventProcessor) Close() error { + if len(this.workerQueue) > 0 { + return fmt.Errorf("EventProcessor.Close(): workerQueue is not empty:%d", len(this.workerQueue)) + } + return nil +} + +func NewEventProcessor(logger *zap.Logger) *EventProcessor { + var ep *EventProcessor + ep = &EventProcessor{} + ep.logger = logger + ep.init() + return ep +} diff --git a/user/common.go b/user/common.go index fbd726da1..483e31e30 100644 --- a/user/common.go +++ b/user/common.go @@ -228,3 +228,14 @@ func dumpByteSlice(b []byte, perfix string) *bytes.Buffer { } return bb } + +func CToGoString(c []byte) string { + n := -1 + for i, b := range c { + if b == 0 { + break + } + n = i + } + return string(c[:n+1]) +} diff --git a/user/event_bash.go b/user/event_bash.go index 4168dad56..9854d7aee 100644 --- a/user/event_bash.go +++ b/user/event_bash.go @@ -76,3 +76,15 @@ func (this *bashEvent) Clone() IEventStruct { func (this *bashEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *bashEvent) GetUUID() string { + return fmt.Sprintf("%d_%d_%s", this.Pid, this.Uid, this.Comm) +} + +func (this *bashEvent) Payload() []byte { + return this.Line[:] +} + +func (this *bashEvent) PayloadLen() int { + return len(this.Line) +} diff --git a/user/event_gnutls.go b/user/event_gnutls.go index d0c198b2e..10eae300d 100644 --- a/user/event_gnutls.go +++ b/user/event_gnutls.go @@ -101,3 +101,16 @@ func (this *GnutlsDataEvent) Clone() IEventStruct { func (this *GnutlsDataEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *GnutlsDataEvent) GetUUID() string { + //return fmt.Sprintf("%d_%d_%s", this.Pid, this.Tid, this.Comm) + return fmt.Sprintf("%d_%d_%s_%d", this.Pid, this.Tid, this.Comm, this.DataType) +} + +func (this *GnutlsDataEvent) Payload() []byte { + return this.Data[:this.Data_len] +} + +func (this *GnutlsDataEvent) PayloadLen() int { + return int(this.Data_len) +} diff --git a/user/event_gossl.go b/user/event_gossl.go index fa1fcb277..8fed9b8c4 100644 --- a/user/event_gossl.go +++ b/user/event_gossl.go @@ -50,3 +50,15 @@ func (e *goSSLEvent) SetModule(m IModule) { func (e *goSSLEvent) EventType() EVENT_TYPE { return EVENT_TYPE_OUTPUT } + +func (this *goSSLEvent) GetUUID() string { + return fmt.Sprintf("%d_%d_%s", this.Pid, this.Tid, this.Comm) +} + +func (this *goSSLEvent) Payload() []byte { + return this.Data[:this.Len] +} + +func (this *goSSLEvent) PayloadLen() int { + return int(this.Len) +} diff --git a/user/event_mysqld.go b/user/event_mysqld.go index c0abb1f5b..4d33a2b30 100644 --- a/user/event_mysqld.go +++ b/user/event_mysqld.go @@ -119,3 +119,15 @@ func (this *mysqldEvent) Clone() IEventStruct { func (this *mysqldEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *mysqldEvent) GetUUID() string { + return fmt.Sprintf("%d_%s", this.Pid, this.comm) +} + +func (this *mysqldEvent) Payload() []byte { + return this.query[:this.len] +} + +func (this *mysqldEvent) PayloadLen() int { + return int(this.len) +} diff --git a/user/event_nspr.go b/user/event_nspr.go index 6ce35f58d..cf8e1a236 100644 --- a/user/event_nspr.go +++ b/user/event_nspr.go @@ -122,3 +122,15 @@ func (this *NsprDataEvent) Clone() IEventStruct { func (this *NsprDataEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *NsprDataEvent) GetUUID() string { + return fmt.Sprintf("%d_%d_%s_%d", this.Pid, this.Tid, this.Comm, this.DataType) +} + +func (this *NsprDataEvent) Payload() []byte { + return this.Data[:this.Data_len] +} + +func (this *NsprDataEvent) PayloadLen() int { + return int(this.Data_len) +} diff --git a/user/event_openssl.go b/user/event_openssl.go index 2a2d51023..4b8e52ebc 100644 --- a/user/event_openssl.go +++ b/user/event_openssl.go @@ -55,7 +55,7 @@ func (t tls_version) String() string { case DTLS1_2_VERSION: return "DTLS1_2_VERSION" } - return "TLS_VERSION_UNKNOW" + return fmt.Sprintf("TLS_VERSION_UNKNOW_%d", t.version) } type SSLDataEvent struct { @@ -105,6 +105,18 @@ func (this *SSLDataEvent) Decode(payload []byte) (err error) { return nil } +func (this *SSLDataEvent) GetUUID() string { + return fmt.Sprintf("%d_%d_%s_%d_%d", this.Pid, this.Tid, CToGoString(this.Comm[:]), this.Fd, this.DataType) +} + +func (this *SSLDataEvent) Payload() []byte { + return this.Data[:this.Data_len] +} + +func (this *SSLDataEvent) PayloadLen() int { + return int(this.Data_len) +} + func (this *SSLDataEvent) StringHex() string { addr := this.module.(*MOpenSSLProbe).GetConn(this.Pid, this.Fd) @@ -124,7 +136,7 @@ func (this *SSLDataEvent) StringHex() string { b.WriteString(COLORRESET) v := tls_version{version: this.Version} - s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, %s, Version:%s, Payload:\n%s", this.Pid, this.Comm, this.Tid, connInfo, v.String(), b.String()) + s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, %s, Version:%s, Payload:\n%s", this.Pid, CToGoString(this.Comm[:]), this.Tid, connInfo, v.String(), b.String()) return s } @@ -143,7 +155,7 @@ func (this *SSLDataEvent) String() string { connInfo = fmt.Sprintf("%sUNKNOW_%d%s", COLORRED, this.DataType, COLORRESET) } v := tls_version{version: this.Version} - s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, Version:%s, %s, Payload:\n%s%s%s", this.Pid, this.Comm, this.Tid, v.String(), connInfo, perfix, string(this.Data[:this.Data_len]), COLORRESET) + s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, Version:%s, %s, Payload:\n%s%s%s", this.Pid, bytes.TrimSpace(this.Comm[:]), this.Tid, v.String(), connInfo, perfix, string(this.Data[:this.Data_len]), COLORRESET) return s } @@ -214,12 +226,12 @@ func (this *ConnDataEvent) Decode(payload []byte) (err error) { } func (this *ConnDataEvent) StringHex() string { - s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, FD:%d, Addr: %s", this.Pid, this.Comm, this.Tid, this.Fd, this.Addr) + s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, FD:%d, Addr: %s", this.Pid, bytes.TrimSpace(this.Comm[:]), this.Tid, this.Fd, this.Addr) return s } func (this *ConnDataEvent) String() string { - s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, FD:%d, Addr: %s", this.Pid, this.Comm, this.Tid, this.Fd, this.Addr) + s := fmt.Sprintf("PID:%d, Comm:%s, TID:%d, FD:%d, Addr: %s", this.Pid, bytes.TrimSpace(this.Comm[:]), this.Tid, this.Fd, this.Addr) return s } @@ -241,3 +253,15 @@ func (this *ConnDataEvent) Clone() IEventStruct { func (this *ConnDataEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *ConnDataEvent) GetUUID() string { + return fmt.Sprintf("%d_%d_%s_%d", this.Pid, this.Tid, bytes.TrimSpace(this.Comm[:]), this.Fd) +} + +func (this *ConnDataEvent) Payload() []byte { + return []byte(this.Addr) +} + +func (this *ConnDataEvent) PayloadLen() int { + return len(this.Addr) +} diff --git a/user/event_postgres.go b/user/event_postgres.go index 8579ee005..7764d89f6 100644 --- a/user/event_postgres.go +++ b/user/event_postgres.go @@ -76,3 +76,15 @@ func (this *postgresEvent) Clone() IEventStruct { func (this *postgresEvent) EventType() EVENT_TYPE { return this.event_type } + +func (this *postgresEvent) GetUUID() string { + return fmt.Sprintf("%d_%s", this.Pid, this.comm) +} + +func (this *postgresEvent) Payload() []byte { + return this.query[:] +} + +func (this *postgresEvent) PayloadLen() int { + return len(this.query) +} diff --git a/user/ievent.go b/user/ievent.go index 4cff0ea3e..819db8a3b 100644 --- a/user/ievent.go +++ b/user/ievent.go @@ -12,6 +12,8 @@ const ( type IEventStruct interface { Decode(payload []byte) (err error) + Payload() []byte + PayloadLen() int String() string StringHex() string Clone() IEventStruct From 4a649ef98b8504cba6c0d58adf83488ff33aa372 Mon Sep 17 00:00:00 2001 From: CFC4N Date: Fri, 1 Jul 2022 22:05:51 +0800 Subject: [PATCH 03/17] add Testdata for unit Testing. Signed-off-by: CFC4N --- pkg/event_processor/http_request.go | 100 +++ pkg/event_processor/http_response.go | 26 + pkg/event_processor/iparser.go | 118 ++++ pkg/event_processor/processor_test.go | 84 +++ .../testdata/952250189026079.bin | 19 + .../testdata/952250202892597.bin | Bin 0 -> 1918 bytes .../testdata/952250203714546.bin | Bin 0 -> 4096 bytes .../testdata/952250205047487.bin | Bin 0 -> 4096 bytes .../testdata/952250214182298.bin | Bin 0 -> 3208 bytes .../testdata/952250215663723.bin | 5 + .../testdata/952250216473699.bin | Bin 0 -> 4096 bytes .../testdata/952250218637446.bin | Bin 0 -> 4096 bytes .../testdata/952250222335617.bin | Bin 0 -> 4096 bytes .../testdata/952250224409516.bin | Bin 0 -> 1304 bytes .../testdata/952250225146211.bin | 2 + .../testdata/952253291192473.bin | 7 + .../testdata/952253597324253.bin | 12 + .../testdata/952253597628796.bin | 4 + .../testdata/952282673103459.bin | 5 + .../testdata/952282712204824.bin | 47 ++ .../testdata/952282715334491.bin | 49 ++ .../testdata/952282731469385.bin | 58 ++ .../testdata/952282736644923.bin | 71 ++ .../testdata/952282737583175.bin | 22 + .../testdata/952293599178141.bin | 5 + .../testdata/952293616935735.bin | 15 + .../testdata/952293617095621.bin | 2 + pkg/event_processor/testdata/all.json | 621 ++++++++++++++++++ 28 files changed, 1272 insertions(+) create mode 100644 pkg/event_processor/http_request.go create mode 100644 pkg/event_processor/http_response.go create mode 100644 pkg/event_processor/iparser.go create mode 100644 pkg/event_processor/processor_test.go create mode 100755 pkg/event_processor/testdata/952250189026079.bin create mode 100755 pkg/event_processor/testdata/952250202892597.bin create mode 100755 pkg/event_processor/testdata/952250203714546.bin create mode 100755 pkg/event_processor/testdata/952250205047487.bin create mode 100755 pkg/event_processor/testdata/952250214182298.bin create mode 100755 pkg/event_processor/testdata/952250215663723.bin create mode 100755 pkg/event_processor/testdata/952250216473699.bin create mode 100755 pkg/event_processor/testdata/952250218637446.bin create mode 100755 pkg/event_processor/testdata/952250222335617.bin create mode 100755 pkg/event_processor/testdata/952250224409516.bin create mode 100755 pkg/event_processor/testdata/952250225146211.bin create mode 100755 pkg/event_processor/testdata/952253291192473.bin create mode 100755 pkg/event_processor/testdata/952253597324253.bin create mode 100755 pkg/event_processor/testdata/952253597628796.bin create mode 100755 pkg/event_processor/testdata/952282673103459.bin create mode 100755 pkg/event_processor/testdata/952282712204824.bin create mode 100755 pkg/event_processor/testdata/952282715334491.bin create mode 100755 pkg/event_processor/testdata/952282731469385.bin create mode 100755 pkg/event_processor/testdata/952282736644923.bin create mode 100755 pkg/event_processor/testdata/952282737583175.bin create mode 100755 pkg/event_processor/testdata/952293599178141.bin create mode 100755 pkg/event_processor/testdata/952293616935735.bin create mode 100755 pkg/event_processor/testdata/952293617095621.bin create mode 100755 pkg/event_processor/testdata/all.json diff --git a/pkg/event_processor/http_request.go b/pkg/event_processor/http_request.go new file mode 100644 index 000000000..692c30305 --- /dev/null +++ b/pkg/event_processor/http_request.go @@ -0,0 +1,100 @@ +package event_processor + +import ( + "bufio" + "bytes" + "net/http" +) + +type HTTPRequest struct { + request *http.Request + parserType PARSER_TYPE + packerType PACKET_TYPE + isDone bool + isInit bool + reader *bytes.Buffer +} + +func (this *HTTPRequest) Body() []byte { + return nil +} + +func (this *HTTPRequest) init() { + +} + +func (this *HTTPRequest) Name() string { + return "HTTPRequest" +} + +func (this *HTTPRequest) PacketType() PACKET_TYPE { + return this.packerType +} + +func (this *HTTPRequest) ParserType() PARSER_TYPE { + return this.parserType +} + +func (this *HTTPRequest) Write(b []byte) (int, error) { + // 如果未初始化 + if !this.isInit { + this.reader = bytes.NewBuffer(b) + buf := bufio.NewReader(this.reader) + req, err := http.ReadRequest(buf) + if err != nil { + return 0, err + } + this.request = req + this.isInit = true + return len(b), nil + } + + // 如果已初始化 + l, e := this.reader.Write(b) + if e != nil { + return 0, e + } + + // TODO 检测是否接收完整个包 + if false { + this.isDone = true + } + + return l, nil +} + +func (this *HTTPRequest) detect(payload []byte) error { + this.init() + rd := bytes.NewReader(payload) + buf := bufio.NewReader(rd) + req, err := http.ReadRequest(buf) + if err != nil { + return err + } + this.parserType = PARSER_TYPE_HTTP_REQUEST + this.request = req + return nil +} + +func (this *HTTPRequest) IsDone() bool { + return this.isDone +} + +func (this *HTTPRequest) Reset() { + this.isDone = false + this.isInit = false + this.reader.Reset() +} + +func (this *HTTPRequest) Display() []byte { + // TODO 获取 http.request的body + + return this.reader.Bytes() + return nil +} + +func init() { + hr := &HTTPRequest{} + hr.reader = bytes.NewBuffer(nil) + Register(hr) +} diff --git a/pkg/event_processor/http_response.go b/pkg/event_processor/http_response.go new file mode 100644 index 000000000..2f1cb3c7f --- /dev/null +++ b/pkg/event_processor/http_response.go @@ -0,0 +1,26 @@ +package event_processor + +import ( + "bufio" + "bytes" + "io" + "io/ioutil" + "net/http" +) + +func readHTTPResponse(payload []byte) (*http.Response, error) { + rd := bytes.NewReader(payload) + buf := bufio.NewReader(rd) + rep := new(http.Request) + resp, err := http.ReadResponse(buf, rep) + if err != nil { + return nil, err + } + + //save response body + b := new(bytes.Buffer) + io.Copy(b, resp.Body) + resp.Body.Close() + resp.Body = ioutil.NopCloser(b) + return resp, nil +} diff --git a/pkg/event_processor/iparser.go b/pkg/event_processor/iparser.go new file mode 100644 index 000000000..381c54de6 --- /dev/null +++ b/pkg/event_processor/iparser.go @@ -0,0 +1,118 @@ +package event_processor + +import ( + "bytes" + "fmt" +) + +type PROCESS_STATUS uint8 +type PACKET_TYPE uint8 +type PARSER_TYPE uint8 + +const ( + PROCESS_STATE_INIT PROCESS_STATUS = iota + PROCESS_STATE_ING + PROCESS_STATE_DONE +) + +const ( + PACKET_TYPE_NULL PACKET_TYPE = iota + PACKET_TYPE_UNKNOW + PACKET_TYPE_GZIP + PACKET_TYPE_WEB_SOCKET +) + +const ( + PARSER_TYPE_NULL PARSER_TYPE = iota + PARSER_TYPE_HTTP_REQUEST + PARSER_TYPE_HTTP_RESPONSE + PARSER_TYPE_WEB_SOCKET +) + +type IParser interface { + detect(b []byte) error + Write(b []byte) (int, error) + ParserType() PARSER_TYPE + PacketType() PACKET_TYPE + Body() []byte + Name() string + IsDone() bool + Display() []byte + Reset() +} + +var parsers = make(map[string]IParser) + +func Register(p IParser) { + if p == nil { + panic("Register Parser is nil") + } + name := p.Name() + if _, dup := parsers[name]; dup { + panic(fmt.Sprintf("Register called twice for Parser %s", name)) + } + parsers[name] = p +} + +// GetModules 获取modules列表 +func GetAllModules() map[string]IParser { + return parsers +} + +func NewParser(payload []byte) IParser { + if len(payload) > 0 { + for _, parser := range GetAllModules() { + err := parser.detect(payload) + if err == nil { + return parser + } + } + } + var np = &NullParser{} + np.reader = bytes.NewBuffer(nil) + return np +} + +type NullParser struct { + reader *bytes.Buffer + isdone bool +} + +func (this *NullParser) Body() []byte { + return this.reader.Bytes() +} + +func (this *NullParser) ParserType() PARSER_TYPE { + return PARSER_TYPE_NULL +} + +func (this *NullParser) PacketType() PACKET_TYPE { + return PACKET_TYPE_NULL +} + +func (this *NullParser) Write(b []byte) (int, error) { + this.isdone = true + return this.reader.Write(b) +} + +// NullParser 检测包类型 +func (this *NullParser) detect(b []byte) error { + return nil +} + +func (this *NullParser) Name() string { + return "NullParser" +} + +func (this *NullParser) IsDone() bool { + return this.isdone +} + +func (this *NullParser) Display() []byte { + return this.Body() +} + +func (this *NullParser) Reset() { + this.isdone = false + this.reader.Reset() +} diff --git a/pkg/event_processor/processor_test.go b/pkg/event_processor/processor_test.go new file mode 100644 index 000000000..d4f5f4b0a --- /dev/null +++ b/pkg/event_processor/processor_test.go @@ -0,0 +1,84 @@ +package event_processor + +import ( + "ecapture/user" + "encoding/json" + "fmt" + "go.uber.org/zap" + "io/ioutil" + "log" + "strings" + "testing" + "time" +) + +var ( + testFile = "testdata/all.json" +) + +type SSLDataEventTmp struct { + //Event_type uint8 `json:"Event_type"` + DataType int64 `json:"DataType"` + Timestamp_ns uint64 `json:"Timestamp_ns"` + Pid uint32 `json:"Pid"` + Tid uint32 `json:"Tid"` + Data_len int32 `json:"Data_len"` + Comm [16]byte `json:"Comm"` + Fd uint32 `json:"Fd"` + Version int32 `json:"Version"` + Data [4096]byte `json:"Data"` +} + +func TestEventProcessor_Serve(t *testing.T) { + + cfg := zap.NewProductionConfig() + cfg.OutputPaths = []string{"stdout", "./output.log"} + cfg.ErrorOutputPaths = []string{"stderr", "./error.log"} + logger, err := cfg.Build() + + ep := NewEventProcessor(logger) + + go func() { + ep.Serve() + }() + content, err := ioutil.ReadFile(testFile) + if err != nil { + //Do something + log.Fatalf("open file error: %s, file:%s", err.Error(), testFile) + } + lines := strings.Split(string(content), "\n") + //log.Println(lines) + var i int + for _, line := range lines { + if line == "" { + continue + } + //ep.Write(user.NewEventStruct(line)) + var event SSLDataEventTmp + err := json.Unmarshal([]byte(line), &event) + if err != nil { + t.Fatalf("json unmarshal error: %s", err.Error()) + } + payloadFile := fmt.Sprintf("testdata/%d.bin", event.Timestamp_ns) + b, e := ioutil.ReadFile(payloadFile) + if e != nil { + t.Fatalf("read payload file error: %s, file:%s", e.Error(), payloadFile) + } + copy(event.Data[:], b) + ep.Write(&user.SSLDataEvent{Data_len: event.Data_len, Data: event.Data, DataType: event.DataType, Timestamp_ns: event.Timestamp_ns, Pid: event.Pid, Tid: event.Tid, Comm: event.Comm, Fd: event.Fd, Version: event.Version}) + i++ + if i > 4 { + break + } + } + + tick := time.NewTicker(time.Second * 6) + select { + case <-tick.C: + } + err = ep.Close() + if err != nil { + log.Fatal(err) + } + log.Println("done") +} diff --git a/pkg/event_processor/testdata/952250189026079.bin b/pkg/event_processor/testdata/952250189026079.bin new file mode 100755 index 000000000..bf42d0007 --- /dev/null +++ b/pkg/event_processor/testdata/952250189026079.bin @@ -0,0 +1,19 @@ +GET / HTTP/1.1 +Host: www.qq.com +Accept-Encoding: deflate, gzip, br +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 +Accept-Language: zh-CN,zh;q=0.9 +Cache-Control: max-age=0 +Connection: keep-alive +Cookie: RK=aRBoGv2qOV; ptcz=98c414ce1bc7b2ba4475a06b77bb0076994450a726ead46734a77a2366097448; tvfe_boss_uuid=24a0550f5b267ce3; pgv_pvid=9174253759; eas_sid=41r6V3t3c9w5V7x8Q8e9l8z4p9; livelink_pvid=4601100288; o_cookie=112633; pac_uid=1_112633; _tc_unionid=62b56677-fab3-4075-989f-65cb525c8bf8; iip=0; fqm_pvqid=c2abbbe0-6649-40b3-bf14-4a6852f065b2; pt_sms_phone=137******44; _clck=1f6vrw9|1|f2e|0; _hjSessionUser_3021617=eyJpZCI6ImE4NDM3NGI4LTlhYTQtNWY1MC1iYTE0LTljMWY4ZTkyZmMwNCIsImNyZWF0ZWQiOjE2NTU0NTkwMTUwNTksImV4aXN0aW5nIjpmYWxzZX0=; pgv_info=ssid=s5965125642; ts_last=www.qq.com/; ts_uid=8120521728; ad_play_index=42; ariaDefaultTheme=undefined +DNT: 1 +Sec-Fetch-Dest: document +Sec-Fetch-Mode: navigate +Sec-Fetch-Site: none +Sec-Fetch-User: ?1 +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36 +sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102" +sec-ch-ua-mobile: ?0 +sec-ch-ua-platform: "macOS" + diff --git a/pkg/event_processor/testdata/952250202892597.bin b/pkg/event_processor/testdata/952250202892597.bin new file mode 100755 index 0000000000000000000000000000000000000000..7081e715417ea4b841083e04612446f40c01bd16 GIT binary patch literal 1918 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 z zIY=*PDIWGOo^xF}Gf#G~RlPGB`}?HR$UAc9q~o6sws#X%)ya?P0~NNy8wA7?yVOte zEgs@mugP^@R`fu=6%u&sDzQP8CNw;*Q6z(MnmXE<>YC(=!AAw?O39Xv*{O-OOIs$O zjS99}J!QFU?c;%c;r$i4EIqNfa0)t{CY-}pq=~vAr*fH|&((fhR9EeCG|m$H)7bV93wQUgbhnFG}XXUa4vT literal 0 HcmV?d00001 diff --git a/pkg/event_processor/testdata/952250205047487.bin b/pkg/event_processor/testdata/952250205047487.bin new file mode 100755 index 0000000000000000000000000000000000000000..b33c2d2a4fe261128e09cb69e13a079c6773677e GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/pkg/event_processor/testdata/952250214182298.bin b/pkg/event_processor/testdata/952250214182298.bin new file mode 100755 index 0000000000000000000000000000000000000000..d3152b912561d89d4c7cdf06e13fd8a48b087fd1 GIT binary patch literal 3208 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Ԫ?OiO0L 36D(oA[zZj_BVs8/sΞm<4NߑMh0 +$G0RNVNOZ@caXiQ{wFڡM)ဴwh|f,\IL]&Ӊ, )7"M~8ZXVl{!@oYuqb H"]pǗ?F4Я&|Vϥw"CIAl;peQi9V%A \ No newline at end of file diff --git a/pkg/event_processor/testdata/952250216473699.bin b/pkg/event_processor/testdata/952250216473699.bin new file mode 100755 index 0000000000000000000000000000000000000000..6465b00411a69fd676bec324469c496b50c18617 GIT binary patch literal 4096 zcmV+b5dZJkQuHI!d|6}>C$Pg9W&iE3RYZIbtaYU|=HLp?N(4m1gR$kUfz4~l>BP{A zsHclx&2Htt8Tu%9t+DS}*-Z}bFs7|E$5I?+b#koL!P30b$+|Eb$J%U|-NsjM1UIP3 zq<&2%k;devH5o`&Q;OUx0A367+ijWl-@JHS+U<|oF}N~;++FEIY?3)5IF&4(1ZcWi zYxG1~4vFJn=sNz&YrBUmVsp$v)v|FV`*q@4B$1kn-wY2X7f0V2UCu}H+3}6psnl@n zMkF!+-u!Hn%8fBjhttmURvSa%JnbSWoTGV~bn=AL=CBbaD>tQIxvBl3+yS50A1pI_ zH+5K9vpGVw`sM2Au8hDM%a^g=AKk$lDo`Dq`VBJ&X+WAx+}gSd*GOEvwvZaTJAA{E zd^DclnpleG7gDn$XXdk!E4gTs3hZz=IVVZmS=!2TRx5`)K}8%zuuiMp&X6vX6`0nq z!1Vr5U>Wb(?sm|c_C1okV(lim(_haOc3Y9L@`uBi-Tu7V2~?I{SK``6nY49s?biHc zc6Da`^Vq}bzh%A+4-$^_t>MAVvzduRcKXcd%GP&dcc-r`eA}b~vre4GU5wM|TAZ1TO`Rb>*V%OBpv z$lt$kc%O{Pnhi5)uTv6Ee~> z?bMC9^t;vP_i`aNh(1LFiM5n9xI#d}rIONzFI!wV)v^P-L}a$GWIbRrT5+9eJ4M7P z5AJ%rPQlFsW7F*RCDzx8!+*N0ug1$Y_P127k(vg*zD{o_$oo6lZbtAZH5ERQR23e- zarm{@)U~UMhO~E6ERQ<1S&g$YzB%a(^zlypNE1eoCJ>P(OhS6~%lxBekS>>v@@L7K#l z=oh)Kk$C|N>tX~P^t?SKc@G76|KU)uTS8wq&Xf_HDMXwplW<qWP+0Lt6vaeEm~v>#9USlu{Vjo@rW z#Mx>R&Z+!T1Q};oShvM+`n`R?R2UXxn-O?zi16A>g7?OSxw)nL$N)>l^@?&|Ute1v zFMy!V7{YcV2-^`6wwr|T+L=@y8DChqxdm*93thGRc)|=n9`N+E;4!E;gb^2>Ky={= z(_HwqGuewxur+&8w_Os-o{Z!^2BiFfPLK&BAQOlno8`S9KaLFX@#DGyJ_XxuSb9k# zz>J&WfZf}?1@iASzZqR8Xpwoz;o8_wKQ|suW4h!oBn{|6b zoFe#_$N61-Z?k=EHrh&&gz}eeK3I+L!HUQSvt0SrE0@MMme5l%VsYKP=n65O69Q8< zIESBmn7SIvA){V;K{xKbg3s_+YBvJ09T8%? zX*sQp;U;og8^gQm;FD%J_-?OT#Rn~_R1ePqH4{7@-VeGj2GF2jn^+Ja)GKr`LBZ?M zDGa2Muz*Ai3rN$#f|p)>30W3ES-p`o=W$gTww>kzVT20=A{Wex8=iaR7%~??S-o6f zcbf^M5hjp`OfW0PIEv1Lqq-Ft24=5u%pb&XrR=kn<*XC!)T!4l3yIbB+cgS1C zi{r;%c=hGybwf=Xftp5y+N>xe@@{lHzqUSs?r9ZY(49>Uovv|jcUt(ZMmS*a*d$9R9)tT%k=qV~tcvoXc z%5V$`Jp>?b$6ij{_dyLFeSXj63H7*~Dj{H9f_@ zJ8|4b*g82YM%i(zi?FeTlja?EhNp2CM>{y)?!;M&WSlOJVLNe}v{N>MpfKHRC5^C^ zL}aU35#!7UU&Z_^)G!pX%cjf9nRHG6Ft@ z2tH+Ec0O`B6Kg6z4`0yD1kTF_4BIW*2*xxb#gG zc4R4ozLrlaZe$5mIW+OL%T9rZr#b0E+hzo98zS0f#em84BeUpU5h!emr&~F_tF#|O zbZjNmODFG?pOnr|ksr%b`m)s-^r)ZQQ*xO>0r68y$L7@RIpu*Yvd_)!-;lZH=Iy@# zg6I=G{UAbSRkwLHls>}9x@ZD%T{L0hy6EG_m&S7cY+~IDEUr5@E#8z+XG1#@zN8U+ zNkn|jTUV4HY6@Ri+~9K3{*XtRd2{Q9nlJ)2fe1BW+5*HT)>6R2x_t;&k5L)MjR1=y z0*jltcjDN~P3@gH_VP~WW6WEu>-2h0mOa9*PS+tDhS_O-oF$A{e*)3^6DC^!jl}qT z6V?e9*R3}F#tB(yrW7r*z|t8^IYz#Mvy%pDIMRn^u zK7lxwPnbBDfBg8w%4nnsV+M=sHD)~H5?s0`^7V0pGQtfCksD?W+s9r!b{Lr#u&`cE zbaQ4bbJWKX(g;ULM2?uYjr-R4#KPau7rlzb^{=uk4@a!8ogpyXjyVWhFT;v}8;<)( zBhCPc=nR;*w(;n(R3X3C#L_xgTrc)Px6`>-BL;4SD>x!oaMRWr&2Klwm(mNn;y#!& zfgcnY&ndx}=S>+=HVRSM%$sOly}k6H36?HB(Cu@d5ST!y$_Xg$Q_+VrZUko>5ofd1 zC-We`7R#qUZelBX<&_;#p=PcP(YzVNit+G_ExQY6FqWejn&zCC4P#l1<1i~m*WL_L zK0r$OJegd)nEWZV(BN8-9wx{(aR*3=YSlF&{+>WUi?4<+1S!>tb}2~p{o~!7co#@< z3PtS_5XH>_HF*PPpiOxg(v#iZVAC1~0n)r7q@jvwyH3qE_33bAVK^C{Ubz)Li?`y_;mJ=dwuQn-bm;2Fr;+98qlv=M4?{ns zwyNvbyx6|>UVE(z=Vf51+$_$>?orXa67qD_gXCkmg=W@5SCz{pnN)9fdyv%4ZglzthakYtG3aT(Ga z4cHzbaPoAhoq?mlDNol4|Ebc+$2%cMYQuf~>zYv;Tq|a43z-M0t?c#H^E2lz{VhCD z2rqB0B~~|9KU^MKoXb3bS0q^C+p+PBA4T4cTp547P}mi5qV-S-cwO(s+q#I22wH{(#QX3s5NoW5f3i3$b_R zbC&UMXMP(0d?LOUoj9}fef+`HyZ>pDBzM1#9zFcWU6&+Uza-H}l0@s2Bn@O#B-1_9?@>V)Cc`b51Je`XchEvPAXe^gZ|0fHQ z?bX*`*mXg&>K7y{k|0@45F{BgwSrX0#ztl@Z=H?h!qYW%{r1T3cU_Qd`US~`BuF+h1gU~`_6*Sg literal 0 HcmV?d00001 diff --git a/pkg/event_processor/testdata/952250218637446.bin b/pkg/event_processor/testdata/952250218637446.bin new file mode 100755 index 0000000000000000000000000000000000000000..b980c1c511fe23d9c0463e8c4ec0aa8ee577311b GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/pkg/event_processor/testdata/952250222335617.bin b/pkg/event_processor/testdata/952250222335617.bin new file mode 100755 index 0000000000000000000000000000000000000000..2dc9a6fa7dc5121044c6c156ac72126e13d99851 GIT binary patch literal 4096 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 yZ9AzO`GM#m$yiY!8N-NZ2W-h#i^z zeCkPKE;dRerS3+>KdvmT4v->@3$i6!i#3joGdThhXvdgO#6Q9yb^t@0o2N#zNWUHd z*+BpNxeOc^ZI?oD3R#pyjQN&rBmC5i3_1lGMD+D8!-U>voFwi_N0+M^cAWNpL8*zq z9l>gcpy~)lC9|A#Fp)=1$U*OhwTSc%57QA16Mn!+4%6Y=W)1t7U2Uc0NYxXCD_RV< z4PchgY-tC$jLQN!UtMK6mNj(1iD*9|OEapbGBnNU6a{ovYIPG8&9R0kD1enzR+Si0 zkrfRnB9H;aX*!eGJBOP9 z=`c!dguX8a8P1$Jf|otuST#~m&=pLC^{_KWLgDEZ(lQltWVEWgvUj>QpO6R*Vo2TiPh?`t``s5{9cK1rtP(tnM~{ZeLKd3>1jYy5c{wU_!uqq^uw=Et2-#gfngXFKh!;1c!xH( z8D{YWnw%y%4M;kxa(h^UE^>mz?s5s5>JxXW3XD17bDW?R`mwD-H<3YRV8g#oaqJZI zK>b8MPxPVi5#3hR*G~k`v9&hg{qB!ze^`DvHA#4BAZ=p+I2p<0*c>3MWfS@bPqwnI z?)Se^?KW24@$~YxMAPW#uwBxT?TwBW1i%3iNOH%TXzw#%sj93F*ZsyQiup;!ynxQw zDEVb7UV^W!Tq<&(LnluiFAN?(@y6-nh1cE~c%^Xc^jmKZ4ZIc|NbrXdkcUNzY9=4l z(3PWZR98sV9rw@zp)ocm4C)mbu!B|8(f|v>SJ}|oQ2SNi#7w|mnLk7Uo3M+0u-1Cn zL${m3t{5z;?@-^i$|^uYL3w=#iM|o60k z069Q~&(VFrt)63HG}q2k$skEs`lW7kWE!Cr1$;mof<{z@pfg~buJsYUy~HWlH_N$l zS+h%-e&TSRp!$i65L%hg)-w1zZZUn851be}e!4S_hxyO|8g61wmY{?()IlnEi+F{q zfo~xWG>55!)c34MWlhOby;>;`8`oalb@LPebgyFB>e&&f + + + + + + + + +CFC4N的博客 – 榫卯江湖,编码人生。 + + + + + + + +