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 {
Name string `json:"name"`
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"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
@ -238,6 +240,16 @@ type FileRecorder struct {
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 {
Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"`
@ -249,6 +261,7 @@ type RedisRecorder struct {
type RecorderObject struct {
Name string `json:"name"`
Record string `json:"record"`
Metadata map[string]any
}
type LimiterConfig struct {

View File

@ -49,6 +49,10 @@ const (
mdKeyPostUp = "postUp"
mdKeyPostDown = "postDown"
mdKeyIgnoreChain = "ignoreChain"
mdKeyRecorderDirection = "direction"
mdKeyRecorderTimestampFormat = "timeStampFormat"
mdKeyRecorderHexdump = "hexdump"
)
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 &&
cfg.Redis.Addr != "" &&
cfg.Redis.Key != "" {

View File

@ -167,9 +167,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
var recorders []recorder.RecorderObject
for _, r := range cfg.Recorders {
md := metadata.NewMetadata(r.Metadata)
recorders = append(recorders, recorder.RecorderObject{
Recorder: registry.RecorderRegistry().Get(r.Name),
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/gin-contrib/cors v1.3.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/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
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/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-gost/core v0.0.0-20230916134612-801f835e9ac1 h1:2B5ir4WZUv5EI+5sKsvSP6GDbrvx/b2+/BnQDPFV1Kg=
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 h1:kqALaNXbbYyKFlcLj3ODsuvzplRxypnJOhMINSiM8sk=
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/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=

View File

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

View File

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

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
}