add recorder

This commit is contained in:
ginuerzh 2022-04-11 23:14:20 +08:00
parent d6f8ec5116
commit 4808441fb3
12 changed files with 301 additions and 27 deletions

View File

@ -180,11 +180,11 @@ func (p *authenticator) parseAuths(r io.Reader) (auths map[string]string, err er
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := strings.TrimSpace(scanner.Text())
if n := strings.IndexByte(line, '#'); n >= 0 { if n := strings.IndexByte(line, '#'); n == 0 {
line = line[:n] continue
} }
sp := strings.SplitN(strings.TrimSpace(line), " ", 2) sp := strings.SplitN(line, " ", 2)
if len(sp) == 1 { if len(sp) == 1 {
if k := strings.TrimSpace(sp[0]); k != "" { if k := strings.TrimSpace(sp[0]); k != "" {
auths[k] = "" auths[k] = ""

View File

@ -115,7 +115,7 @@ type FileLoader struct {
} }
type RedisLoader struct { type RedisLoader struct {
Addr string `yaml:",omitempty" json:"addr,omitempty"` Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"` DB int `yaml:",omitempty" json:"db,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"` Password string `yaml:",omitempty" json:"password,omitempty"`
Key string `yaml:",omitempty" json:"key,omitempty"` Key string `yaml:",omitempty" json:"key,omitempty"`
@ -132,9 +132,7 @@ type NameserverConfig struct {
} }
type ResolverConfig struct { type ResolverConfig struct {
Name string `json:"name"` Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Nameservers []*NameserverConfig `json:"nameservers"` Nameservers []*NameserverConfig `json:"nameservers"`
} }
@ -145,12 +143,34 @@ type HostMappingConfig struct {
} }
type HostsConfig struct { type HostsConfig struct {
Name string `json:"name"` Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Mappings []*HostMappingConfig `json:"mappings"` Mappings []*HostMappingConfig `json:"mappings"`
} }
type RecorderConfig struct {
Name string `json:"name"`
File *FileRecorder `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisRecorder `yaml:",omitempty" json:"redis,omitempty"`
}
type FileRecorder struct {
Path string `json:"path"`
Sep string `yaml:",omitempty" json:"sep,omitempty"`
}
type RedisRecorder struct {
Addr string `json:"addr"`
DB int `yaml:",omitempty" json:"db,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
Key string `yaml:",omitempty" json:"key,omitempty"`
Type string `yaml:",omitempty" json:"type,omitempty"`
}
type RecorderObject struct {
Name string `json:"name"`
Record string `json:"record"`
}
type ListenerConfig struct { type ListenerConfig struct {
Type string `json:"type"` Type string `json:"type"`
Chain string `yaml:",omitempty" json:"chain,omitempty"` Chain string `yaml:",omitempty" json:"chain,omitempty"`
@ -194,17 +214,18 @@ type SockOptsConfig struct {
} }
type ServiceConfig struct { type ServiceConfig struct {
Name string `json:"name"` Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"` Addr string `yaml:",omitempty" json:"addr,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"` Interface string `yaml:",omitempty" json:"interface,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"` SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Admission string `yaml:",omitempty" json:"admission,omitempty"` Admission string `yaml:",omitempty" json:"admission,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"` Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"` Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"` Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"` Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"` Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"` Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
} }
type ChainConfig struct { type ChainConfig struct {
@ -244,6 +265,7 @@ type Config struct {
Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"` Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"`
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"` Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"` Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
Recorders []*RecorderConfig `yaml:",omitempty" json:"recorders,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"` TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"` Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"` Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`

View File

@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hosts" "github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/resolver" "github.com/go-gost/core/resolver"
admission_impl "github.com/go-gost/x/admission" admission_impl "github.com/go-gost/x/admission"
auth_impl "github.com/go-gost/x/auth" auth_impl "github.com/go-gost/x/auth"
@ -17,6 +18,7 @@ import (
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
hosts_impl "github.com/go-gost/x/hosts" hosts_impl "github.com/go-gost/x/hosts"
"github.com/go-gost/x/internal/loader" "github.com/go-gost/x/internal/loader"
recorder_impl "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
resolver_impl "github.com/go-gost/x/resolver" resolver_impl "github.com/go-gost/x/resolver"
) )
@ -211,3 +213,35 @@ func ParseHosts(cfg *config.HostsConfig) hosts.HostMapper {
} }
return hosts return hosts
} }
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
if cfg == nil {
return nil
}
if cfg.File != nil && cfg.File.Path != "" {
return recorder_impl.FileRecorder(cfg.File.Path,
recorder_impl.SepRecorderOption(cfg.File.Sep))
}
if cfg.Redis != nil &&
cfg.Redis.Addr != "" &&
cfg.Redis.Key != "" {
switch cfg.Redis.Type {
case "list": // redis list
return recorder_impl.RedisListRecorder(cfg.Redis.Addr,
recorder_impl.DBRedisRecorderOption(cfg.Redis.DB),
recorder_impl.KeyRedisRecorderOption(cfg.Redis.Key),
recorder_impl.PasswordRedisRecorderOption(cfg.Redis.Password),
)
default: // redis set
return recorder_impl.RedisSetRecorder(cfg.Redis.Addr,
recorder_impl.DBRedisRecorderOption(cfg.Redis.DB),
recorder_impl.KeyRedisRecorderOption(cfg.Redis.Key),
recorder_impl.PasswordRedisRecorderOption(cfg.Redis.Password),
)
}
}
return
}

View File

@ -7,6 +7,7 @@ import (
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/listener" "github.com/go-gost/core/listener"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/service" "github.com/go-gost/core/service"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
tls_util "github.com/go-gost/x/internal/util/tls" tls_util "github.com/go-gost/x/internal/util/tls"
@ -104,6 +105,13 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
} }
} }
var recorders []recorder.RecorderObject
for _, r := range cfg.Recorders {
recorders = append(recorders, recorder.RecorderObject{
Recorder: registry.RecorderRegistry().Get(r.Name),
Record: r.Record,
})
}
router := (&chain.Router{}). router := (&chain.Router{}).
WithRetries(cfg.Handler.Retries). WithRetries(cfg.Handler.Retries).
// WithTimeout(timeout time.Duration). // WithTimeout(timeout time.Duration).
@ -112,6 +120,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
WithChain(registry.ChainRegistry().Get(cfg.Handler.Chain)). WithChain(registry.ChainRegistry().Get(cfg.Handler.Chain)).
WithResolver(registry.ResolverRegistry().Get(cfg.Resolver)). WithResolver(registry.ResolverRegistry().Get(cfg.Resolver)).
WithHosts(registry.HostsRegistry().Get(cfg.Hosts)). WithHosts(registry.HostsRegistry().Get(cfg.Hosts)).
WithRecorder(recorders...).
WithLogger(handlerLogger) WithLogger(handlerLogger)
h := registry.HandlerRegistry().Get(cfg.Handler.Type)( h := registry.HandlerRegistry().Get(cfg.Handler.Type)(

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/docker/libcontainer v2.2.1+incompatible github.com/docker/libcontainer v2.2.1+incompatible
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.7 github.com/gin-gonic/gin v1.7.7
github.com/go-gost/core v0.0.0-20220408131722-a117222cde09 github.com/go-gost/core v0.0.0-20220411145302-03988fee0b9a
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/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7

4
go.sum
View File

@ -121,8 +121,8 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm
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-20220408131722-a117222cde09 h1:NJJ2AXxnEBEYGL+GFVQ3dLZvYkHDFhxQIpcdNMw0gI4= github.com/go-gost/core v0.0.0-20220411145302-03988fee0b9a h1:GYdVcUmEoFVzkPNDavMsDnGA6AM+jgORE4n3/tIiKqI=
github.com/go-gost/core v0.0.0-20220408131722-a117222cde09/go.mod h1:bHVbCS9da6XtKNYMkMUVcck5UqDDUkyC37erVfs4GXQ= github.com/go-gost/core v0.0.0-20220411145302-03988fee0b9a/go.mod h1:bHVbCS9da6XtKNYMkMUVcck5UqDDUkyC37erVfs4GXQ=
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

@ -11,6 +11,7 @@ type fileLoader struct {
filename string filename string
} }
// FileLoader loads data from file.
func FileLoader(filename string) Loader { func FileLoader(filename string) Loader {
return &fileLoader{ return &fileLoader{
filename: filename, filename: filename,

View File

@ -45,7 +45,7 @@ type redisSetLoader struct {
key string key string
} }
// RedisSetLoader loads values from redis set. // RedisSetLoader loads data from redis set.
func RedisSetLoader(addr string, opts ...RedisLoaderOption) Loader { func RedisSetLoader(addr string, opts ...RedisLoaderOption) Loader {
var options redisLoaderOptions var options redisLoaderOptions
for _, opt := range opts { for _, opt := range opts {
@ -84,7 +84,7 @@ type redisHashLoader struct {
key string key string
} }
// RedisHashLoader loads values from redis hash. // RedisHashLoader loads data from redis hash.
func RedisHashLoader(addr string, opts ...RedisLoaderOption) Loader { func RedisHashLoader(addr string, opts ...RedisLoaderOption) Loader {
var options redisLoaderOptions var options redisLoaderOptions
for _, opt := range opts { for _, opt := range opts {

59
recorder/file.go Normal file
View File

@ -0,0 +1,59 @@
package recorder
import (
"context"
"os"
"github.com/go-gost/core/recorder"
)
type fileRecorderOptions struct {
sep string
}
type FileRecorderOption func(opts *fileRecorderOptions)
func SepRecorderOption(sep string) FileRecorderOption {
return func(opts *fileRecorderOptions) {
opts.sep = sep
}
}
type fileRecorder struct {
filename string
sep string
}
// FileRecorder records data to file.
func FileRecorder(filename string, opts ...FileRecorderOption) recorder.Recorder {
var options fileRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &fileRecorder{
filename: filename,
sep: options.sep,
}
}
func (r *fileRecorder) Record(ctx context.Context, b []byte) error {
f, err := os.OpenFile(r.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
if _, err = f.Write(b); err != nil {
return err
}
if r.sep != "" {
_, err := f.WriteString(r.sep)
return err
}
return nil
}
func (r *fileRecorder) Close() error {
return nil
}

101
recorder/redis.go Normal file
View File

@ -0,0 +1,101 @@
package recorder
import (
"context"
"github.com/go-gost/core/recorder"
"github.com/go-redis/redis/v8"
)
type redisRecorderOptions struct {
db int
password string
key string
}
type RedisRecorderOption func(opts *redisRecorderOptions)
func DBRedisRecorderOption(db int) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.db = db
}
}
func PasswordRedisRecorderOption(password string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.password = password
}
}
func KeyRedisRecorderOption(key string) RedisRecorderOption {
return func(opts *redisRecorderOptions) {
opts.key = key
}
}
type redisSetRecorder struct {
client *redis.Client
key string
}
// RedisSetRecorder records data to a redis set.
func RedisSetRecorder(addr string, opts ...RedisRecorderOption) recorder.Recorder {
var options redisRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &redisSetRecorder{
client: redis.NewClient(&redis.Options{
Addr: addr,
Password: options.password,
DB: options.db,
}),
key: options.key,
}
}
func (r *redisSetRecorder) Record(ctx context.Context, b []byte) error {
if r.key == "" {
return nil
}
return r.client.SAdd(ctx, r.key, b).Err()
}
func (r *redisSetRecorder) Close() error {
return r.client.Close()
}
type redisListRecorder struct {
client *redis.Client
key string
}
// RedisListRecorder records data to a redis list.
func RedisListRecorder(addr string, opts ...RedisRecorderOption) recorder.Recorder {
var options redisRecorderOptions
for _, opt := range opts {
opt(&options)
}
return &redisListRecorder{
client: redis.NewClient(&redis.Options{
Addr: addr,
Password: options.password,
DB: options.db,
}),
key: options.key,
}
}
func (r *redisListRecorder) Record(ctx context.Context, b []byte) error {
if r.key == "" {
return nil
}
return r.client.LPush(ctx, r.key, b).Err()
}
func (r *redisListRecorder) Close() error {
return r.client.Close()
}

42
registry/recorder.go Normal file
View File

@ -0,0 +1,42 @@
package registry
import (
"context"
"github.com/go-gost/core/recorder"
)
type recorderRegistry struct {
registry
}
func (r *recorderRegistry) Register(name string, v recorder.Recorder) error {
return r.registry.Register(name, v)
}
func (r *recorderRegistry) Get(name string) recorder.Recorder {
if name != "" {
return &recorderWrapper{name: name, r: r}
}
return nil
}
func (r *recorderRegistry) get(name string) recorder.Recorder {
if v := r.registry.Get(name); v != nil {
return v.(recorder.Recorder)
}
return nil
}
type recorderWrapper struct {
name string
r *recorderRegistry
}
func (w *recorderWrapper) Record(ctx context.Context, b []byte) error {
v := w.r.get(w.name)
if v == nil {
return nil
}
return v.Record(ctx, b)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/bypass" "github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hosts" "github.com/go-gost/core/hosts"
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/resolver" "github.com/go-gost/core/resolver"
"github.com/go-gost/core/service" "github.com/go-gost/core/service"
) )
@ -31,6 +32,7 @@ var (
bypassReg Registry[bypass.Bypass] = &bypassRegistry{} bypassReg Registry[bypass.Bypass] = &bypassRegistry{}
resolverReg Registry[resolver.Resolver] = &resolverRegistry{} resolverReg Registry[resolver.Resolver] = &resolverRegistry{}
hostsReg Registry[hosts.HostMapper] = &hostsRegistry{} hostsReg Registry[hosts.HostMapper] = &hostsRegistry{}
recorderReg Registry[recorder.Recorder] = &recorderRegistry{}
) )
type Registry[T any] interface { type Registry[T any] interface {
@ -120,3 +122,7 @@ func ResolverRegistry() Registry[resolver.Resolver] {
func HostsRegistry() Registry[hosts.HostMapper] { func HostsRegistry() Registry[hosts.HostMapper] {
return hostsReg return hostsReg
} }
func RecorderRegistry() Registry[recorder.Recorder] {
return recorderReg
}