add recorder
This commit is contained in:
parent
d6f8ec5116
commit
4808441fb3
@ -180,11 +180,11 @@ func (p *authenticator) parseAuths(r io.Reader) (auths map[string]string, err er
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if n := strings.IndexByte(line, '#'); n >= 0 {
|
||||
line = line[:n]
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if n := strings.IndexByte(line, '#'); n == 0 {
|
||||
continue
|
||||
}
|
||||
sp := strings.SplitN(strings.TrimSpace(line), " ", 2)
|
||||
sp := strings.SplitN(line, " ", 2)
|
||||
if len(sp) == 1 {
|
||||
if k := strings.TrimSpace(sp[0]); k != "" {
|
||||
auths[k] = ""
|
||||
|
@ -115,7 +115,7 @@ type FileLoader struct {
|
||||
}
|
||||
|
||||
type RedisLoader struct {
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
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"`
|
||||
@ -133,8 +133,6 @@ type NameserverConfig struct {
|
||||
|
||||
type ResolverConfig struct {
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Nameservers []*NameserverConfig `json:"nameservers"`
|
||||
}
|
||||
|
||||
@ -146,11 +144,33 @@ type HostMappingConfig struct {
|
||||
|
||||
type HostsConfig struct {
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
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 string `json:"type"`
|
||||
Chain string `yaml:",omitempty" json:"chain,omitempty"`
|
||||
@ -202,6 +222,7 @@ type ServiceConfig struct {
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
|
||||
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
|
||||
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
|
||||
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
|
||||
@ -244,6 +265,7 @@ type Config struct {
|
||||
Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
|
||||
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Recorders []*RecorderConfig `yaml:",omitempty" json:"recorders,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
|
||||
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hosts"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/resolver"
|
||||
admission_impl "github.com/go-gost/x/admission"
|
||||
auth_impl "github.com/go-gost/x/auth"
|
||||
@ -17,6 +18,7 @@ import (
|
||||
"github.com/go-gost/x/config"
|
||||
hosts_impl "github.com/go-gost/x/hosts"
|
||||
"github.com/go-gost/x/internal/loader"
|
||||
recorder_impl "github.com/go-gost/x/recorder"
|
||||
"github.com/go-gost/x/registry"
|
||||
resolver_impl "github.com/go-gost/x/resolver"
|
||||
)
|
||||
@ -211,3 +213,35 @@ func ParseHosts(cfg *config.HostsConfig) hosts.HostMapper {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/service"
|
||||
"github.com/go-gost/x/config"
|
||||
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{}).
|
||||
WithRetries(cfg.Handler.Retries).
|
||||
// WithTimeout(timeout time.Duration).
|
||||
@ -112,6 +120,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
WithChain(registry.ChainRegistry().Get(cfg.Handler.Chain)).
|
||||
WithResolver(registry.ResolverRegistry().Get(cfg.Resolver)).
|
||||
WithHosts(registry.HostsRegistry().Get(cfg.Hosts)).
|
||||
WithRecorder(recorders...).
|
||||
WithLogger(handlerLogger)
|
||||
|
||||
h := registry.HandlerRegistry().Get(cfg.Handler.Type)(
|
||||
|
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/docker/libcontainer v2.2.1+incompatible
|
||||
github.com/gin-contrib/cors v1.3.1
|
||||
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/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
|
||||
|
4
go.sum
4
go.sum
@ -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/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-20220408131722-a117222cde09 h1:NJJ2AXxnEBEYGL+GFVQ3dLZvYkHDFhxQIpcdNMw0gI4=
|
||||
github.com/go-gost/core v0.0.0-20220408131722-a117222cde09/go.mod h1:bHVbCS9da6XtKNYMkMUVcck5UqDDUkyC37erVfs4GXQ=
|
||||
github.com/go-gost/core v0.0.0-20220411145302-03988fee0b9a h1:GYdVcUmEoFVzkPNDavMsDnGA6AM+jgORE4n3/tIiKqI=
|
||||
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/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
|
||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
|
||||
|
@ -11,6 +11,7 @@ type fileLoader struct {
|
||||
filename string
|
||||
}
|
||||
|
||||
// FileLoader loads data from file.
|
||||
func FileLoader(filename string) Loader {
|
||||
return &fileLoader{
|
||||
filename: filename,
|
||||
|
@ -45,7 +45,7 @@ type redisSetLoader struct {
|
||||
key string
|
||||
}
|
||||
|
||||
// RedisSetLoader loads values from redis set.
|
||||
// RedisSetLoader loads data from redis set.
|
||||
func RedisSetLoader(addr string, opts ...RedisLoaderOption) Loader {
|
||||
var options redisLoaderOptions
|
||||
for _, opt := range opts {
|
||||
@ -84,7 +84,7 @@ type redisHashLoader struct {
|
||||
key string
|
||||
}
|
||||
|
||||
// RedisHashLoader loads values from redis hash.
|
||||
// RedisHashLoader loads data from redis hash.
|
||||
func RedisHashLoader(addr string, opts ...RedisLoaderOption) Loader {
|
||||
var options redisLoaderOptions
|
||||
for _, opt := range opts {
|
||||
|
59
recorder/file.go
Normal file
59
recorder/file.go
Normal 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
101
recorder/redis.go
Normal 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
42
registry/recorder.go
Normal 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)
|
||||
}
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hosts"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/resolver"
|
||||
"github.com/go-gost/core/service"
|
||||
)
|
||||
@ -31,6 +32,7 @@ var (
|
||||
bypassReg Registry[bypass.Bypass] = &bypassRegistry{}
|
||||
resolverReg Registry[resolver.Resolver] = &resolverRegistry{}
|
||||
hostsReg Registry[hosts.HostMapper] = &hostsRegistry{}
|
||||
recorderReg Registry[recorder.Recorder] = &recorderRegistry{}
|
||||
)
|
||||
|
||||
type Registry[T any] interface {
|
||||
@ -120,3 +122,7 @@ func ResolverRegistry() Registry[resolver.Resolver] {
|
||||
func HostsRegistry() Registry[hosts.HostMapper] {
|
||||
return hostsReg
|
||||
}
|
||||
|
||||
func RecorderRegistry() Registry[recorder.Recorder] {
|
||||
return recorderReg
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user