add recorder for serial handler

This commit is contained in:
ginuerzh 2023-09-18 21:13:11 +08:00
parent a623232cc1
commit a743862f23
9 changed files with 188 additions and 26 deletions

View File

@ -229,6 +229,8 @@ type IngressConfig struct {
type RecorderConfig struct { type RecorderConfig struct {
Name string `json:"name"` Name string `json:"name"`
File *FileRecorder `yaml:",omitempty" json:"file,omitempty"` File *FileRecorder `yaml:",omitempty" json:"file,omitempty"`
TCP *TCPRecorder `yaml:"tcp,omitempty" json:"tcp,omitempty"`
HTTP *HTTPRecorder `yaml:"http,omitempty" json:"http,omitempty"`
Redis *RedisRecorder `yaml:",omitempty" json:"redis,omitempty"` Redis *RedisRecorder `yaml:",omitempty" json:"redis,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"` Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
} }
@ -238,6 +240,16 @@ type FileRecorder struct {
Sep string `yaml:",omitempty" json:"sep,omitempty"` Sep string `yaml:",omitempty" json:"sep,omitempty"`
} }
type TCPRecorder struct {
Addr string `json:"addr"`
Timeout time.Duration `json:"timeout"`
}
type HTTPRecorder struct {
URL string `json:"url" yaml:"url"`
Timeout time.Duration `json:"timeout"`
}
type RedisRecorder struct { type RedisRecorder struct {
Addr string `json:"addr"` Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"` DB int `yaml:",omitempty" json:"db,omitempty"`
@ -247,8 +259,9 @@ type RedisRecorder struct {
} }
type RecorderObject struct { type RecorderObject struct {
Name string `json:"name"` Name string `json:"name"`
Record string `json:"record"` Record string `json:"record"`
Metadata map[string]any
} }
type LimiterConfig struct { type LimiterConfig struct {

View File

@ -49,6 +49,10 @@ const (
mdKeyPostUp = "postUp" mdKeyPostUp = "postUp"
mdKeyPostDown = "postDown" mdKeyPostDown = "postDown"
mdKeyIgnoreChain = "ignoreChain" mdKeyIgnoreChain = "ignoreChain"
mdKeyRecorderDirection = "direction"
mdKeyRecorderTimestampFormat = "timeStampFormat"
mdKeyRecorderHexdump = "hexdump"
) )
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator { func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
@ -492,6 +496,14 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
) )
} }
if cfg.TCP != nil && cfg.TCP.Addr != "" {
return xrecorder.TCPRecorder(cfg.TCP.Addr, xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
return xrecorder.HTTPRecorder(cfg.HTTP.URL, xrecorder.TimeoutHTTPRecorderOption(cfg.HTTP.Timeout))
}
if cfg.Redis != nil && if cfg.Redis != nil &&
cfg.Redis.Addr != "" && cfg.Redis.Addr != "" &&
cfg.Redis.Key != "" { cfg.Redis.Key != "" {

View File

@ -167,9 +167,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
var recorders []recorder.RecorderObject var recorders []recorder.RecorderObject
for _, r := range cfg.Recorders { for _, r := range cfg.Recorders {
md := metadata.NewMetadata(r.Metadata)
recorders = append(recorders, recorder.RecorderObject{ recorders = append(recorders, recorder.RecorderObject{
Recorder: registry.RecorderRegistry().Get(r.Name), Recorder: registry.RecorderRegistry().Get(r.Name),
Record: r.Record, Record: r.Record,
Options: &recorder.Options{
Direction: mdutil.GetBool(md, mdKeyRecorderDirection),
TimestampFormat: mdutil.GetString(md, mdKeyRecorderTimestampFormat),
Hexdump: mdutil.GetBool(md, mdKeyRecorderHexdump),
},
}) })
} }

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.0.0-20230916134612-801f835e9ac1 github.com/go-gost/core v0.0.0-20230918131208-c258a114c40b
github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
github.com/go-gost/plugin v0.0.0-20230418123101-d221a4ec9a98 github.com/go-gost/plugin v0.0.0-20230418123101-d221a4ec9a98

4
go.sum
View File

@ -99,8 +99,8 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gost/core v0.0.0-20230916134612-801f835e9ac1 h1:2B5ir4WZUv5EI+5sKsvSP6GDbrvx/b2+/BnQDPFV1Kg= github.com/go-gost/core v0.0.0-20230918131208-c258a114c40b h1:kqALaNXbbYyKFlcLj3ODsuvzplRxypnJOhMINSiM8sk=
github.com/go-gost/core v0.0.0-20230916134612-801f835e9ac1/go.mod h1:db6lBY+DkC3ct4OJfclsKnQwQmcv1B9NnMnpI2MNUwY= github.com/go-gost/core v0.0.0-20230918131208-c258a114c40b/go.mod h1:db6lBY+DkC3ct4OJfclsKnQwQmcv1B9NnMnpI2MNUwY=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s= github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc= github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04= github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=

View File

@ -12,33 +12,52 @@ import (
type recorderConn struct { type recorderConn struct {
net.Conn net.Conn
recorder recorder.Recorder recorder recorder.RecorderObject
} }
func (c *recorderConn) Read(b []byte) (n int, err error) { func (c *recorderConn) Read(b []byte) (n int, err error) {
n, err = c.Conn.Read(b) n, err = c.Conn.Read(b)
if n > 0 && c.recorder != nil { if n > 0 && c.recorder.Recorder != nil {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteByte('>') if c.recorder.Options != nil && c.recorder.Options.Direction {
buf.WriteString(time.Now().Format("2006-01-02 15:04:05.000")) buf.WriteByte('>')
buf.WriteByte('\n') }
buf.WriteString(hex.Dump(b[:n])) if c.recorder.Options != nil && c.recorder.Options.TimestampFormat != "" {
c.recorder.Record(context.Background(), buf.Bytes()) buf.WriteString(time.Now().Format(c.recorder.Options.TimestampFormat))
}
if buf.Len() > 0 {
buf.WriteByte('\n')
}
if c.recorder.Options != nil && c.recorder.Options.Hexdump {
buf.WriteString(hex.Dump(b[:n]))
} else {
buf.Write(b[:n])
}
c.recorder.Recorder.Record(context.Background(), buf.Bytes())
} }
return return
} }
func (c *recorderConn) Write(b []byte) (int, error) { func (c *recorderConn) Write(b []byte) (int, error) {
if c.recorder != nil { if c.recorder.Recorder != nil {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteByte('<') if c.recorder.Options != nil && c.recorder.Options.Direction {
buf.WriteString(time.Now().Format("2006-01-02 15:04:05.000")) buf.WriteByte('<')
buf.WriteByte('\n') }
buf.WriteString(hex.Dump(b)) if c.recorder.Options != nil && c.recorder.Options.TimestampFormat != "" {
c.recorder.Record(context.Background(), buf.Bytes()) buf.WriteString(time.Now().Format(c.recorder.Options.TimestampFormat))
}
if buf.Len() > 0 {
buf.WriteByte('\n')
}
if c.recorder.Options != nil && c.recorder.Options.Hexdump {
buf.WriteString(hex.Dump(b))
} else {
buf.Write(b)
}
c.recorder.Recorder.Record(context.Background(), buf.Bytes())
} }
return c.Conn.Write(b) return c.Conn.Write(b)
} }

View File

@ -28,7 +28,7 @@ type serialHandler struct {
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
recorder recorder.Recorder recorder recorder.RecorderObject
} }
func NewHandler(opts ...handler.Option) handler.Handler { func NewHandler(opts ...handler.Option) handler.Handler {
@ -54,7 +54,7 @@ func (h *serialHandler) Init(md md.Metadata) (err error) {
if opts := h.router.Options(); opts != nil { if opts := h.router.Options(); opts != nil {
for _, ro := range opts.Recorders { for _, ro := range opts.Recorders {
if ro.Record == xrecorder.RecorderServiceHandlerSerial { if ro.Record == xrecorder.RecorderServiceHandlerSerial {
h.recorder = ro.Recorder h.recorder = ro
break break
} }
} }
@ -96,11 +96,9 @@ func (h *serialHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
log.Debugf("%s >> %s", conn.LocalAddr(), target.Addr) log.Debugf("%s >> %s", conn.LocalAddr(), target.Addr)
if h.recorder != nil { conn = &recorderConn{
conn = &recorderConn{ Conn: conn,
Conn: conn, recorder: h.recorder,
recorder: h.recorder,
}
} }
// serial port // serial port

62
recorder/http.go Normal file
View File

@ -0,0 +1,62 @@
package recorder
import (
"bytes"
"context"
"fmt"
"net/http"
"time"
"github.com/go-gost/core/recorder"
)
type httpRecorderOptions struct {
timeout time.Duration
}
type HTTPRecorderOption func(opts *httpRecorderOptions)
func TimeoutHTTPRecorderOption(timeout time.Duration) HTTPRecorderOption {
return func(opts *httpRecorderOptions) {
opts.timeout = timeout
}
}
type httpRecorder struct {
url string
httpClient *http.Client
}
// HTTPRecorder records data to HTTP service.
func HTTPRecorder(url string, opts ...HTTPRecorderOption) recorder.Recorder {
var options httpRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &httpRecorder{
url: url,
httpClient: &http.Client{
Timeout: options.timeout,
},
}
}
func (r *httpRecorder) Record(ctx context.Context, b []byte) error {
req, err := http.NewRequest(http.MethodPost, r.url, bytes.NewReader(b))
if err != nil {
return err
}
resp, err := r.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%d %s", resp.StatusCode, resp.Status)
}
return nil
}

52
recorder/tcp.go Normal file
View File

@ -0,0 +1,52 @@
package recorder
import (
"context"
"net"
"time"
"github.com/go-gost/core/recorder"
)
type tcpRecorderOptions struct {
timeout time.Duration
}
type TCPRecorderOption func(opts *tcpRecorderOptions)
func TimeoutTCPRecorderOption(timeout time.Duration) TCPRecorderOption {
return func(opts *tcpRecorderOptions) {
opts.timeout = timeout
}
}
type tcpRecorder struct {
addr string
dialer *net.Dialer
}
// TCPRecorder records data to TCP service.
func TCPRecorder(addr string, opts ...TCPRecorderOption) recorder.Recorder {
var options tcpRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &tcpRecorder{
addr: addr,
dialer: &net.Dialer{
Timeout: options.timeout,
},
}
}
func (r *tcpRecorder) Record(ctx context.Context, b []byte) error {
c, err := r.dialer.DialContext(ctx, "tcp", r.addr)
if err != nil {
return err
}
defer c.Close()
_, err = c.Write(b)
return err
}