mirror of
https://github.com/go-gost/x.git
synced 2026-10-20 00:17:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07ca57055a | ||
|
|
2acc669976 | ||
|
|
6c32dda058 | ||
|
|
2a2643a6b7 | ||
|
|
802943bd28 | ||
|
|
f373f2f4b9 | ||
|
|
96f35bd7aa | ||
|
|
cd7bf9521f | ||
|
|
180145189f | ||
|
|
7625973ca1 | ||
|
|
b3b5986b63 | ||
|
|
254699e176 | ||
|
|
6389378610 | ||
|
|
77eda4ce00 | ||
|
|
a280193dac | ||
|
|
0c97db3c05 | ||
|
|
d2cc07d3ac | ||
|
|
a0c46cec6e | ||
|
|
eae322c7ba | ||
|
|
1c1f092a14 | ||
|
|
a69a759e17 | ||
|
|
007ff0bae9 | ||
|
|
e2072399dd | ||
|
|
c7d16962ec | ||
|
|
a12ed27dfa | ||
|
|
bf70f07419 | ||
|
|
a65b7a059a | ||
|
|
d5683e9138 | ||
|
|
52289bea6c | ||
|
|
d0ff5358df | ||
|
|
0e5c285eb7 | ||
|
|
a5f66b0615 | ||
|
|
f3d3d2231e | ||
|
|
4eb006f1b0 | ||
|
|
2b9f795972 | ||
|
|
589c185053 | ||
|
|
404aaae5f1 | ||
|
|
fd9dc6408a | ||
|
|
b597467858 | ||
|
|
ad5cf6fd61 | ||
|
|
f71351f5ef | ||
|
|
79203e407c | ||
|
|
db21de831a | ||
|
|
a5309eee97 | ||
|
|
b64e5901b6 | ||
|
|
87e454a6f1 | ||
|
|
3bbc10796c | ||
|
|
208d18125c | ||
|
|
35a049fb03 | ||
|
|
281295d02f | ||
|
|
acae52ab89 | ||
|
|
afcf4a5252 | ||
|
|
d9a2f44a78 | ||
|
|
97ed5080a6 | ||
|
|
c8d905dea5 | ||
|
|
fe096d6e74 | ||
|
|
3ec2f9d487 | ||
|
|
ce3c28baf5 | ||
|
|
11d982e062 | ||
|
|
6caad5e37e | ||
|
|
d819fd3041 | ||
|
|
8cacfad9eb | ||
|
|
a71b9d79fd | ||
|
|
9a380d3fd8 | ||
|
|
4c99e11d81 | ||
|
|
1eaf08c9c1 | ||
|
|
0e59ccc64b | ||
|
|
04d1587a77 | ||
|
|
69a0aa8853 | ||
|
|
e0915affda | ||
|
|
d610e2dc01 | ||
|
|
2495b13053 | ||
|
|
11bca5bc3d | ||
|
|
e4b1c3c2b5 | ||
|
|
75a106ee84 | ||
|
|
2132268780 | ||
|
|
ef440e8b4a | ||
|
|
fc12e33786 | ||
|
|
08ad260606 | ||
|
|
9cd03b6b59 | ||
|
|
355c72477a | ||
|
|
379d9a73ad | ||
|
|
bf36bdc680 | ||
|
|
aa7528fc1e | ||
|
|
e480039691 | ||
|
|
489a762811 | ||
|
|
eef3bd7f25 | ||
|
|
06d993023f | ||
|
|
914a4622fd | ||
|
|
a5d4774682 | ||
|
|
be8e0555e5 | ||
|
|
2693ba61c4 | ||
|
|
fcb54bd576 | ||
|
|
5206d1bd58 | ||
|
|
98f5a5f7eb | ||
|
|
ff6807815f | ||
|
|
8d962398ab | ||
|
|
c1e6164a83 | ||
|
|
6f001a779a | ||
|
|
2e8a5d6f51 | ||
|
|
c2aedbabd9 | ||
|
|
79b6b9138e | ||
|
|
3db20563d2 | ||
|
|
a12870e766 | ||
|
|
edb9a992e2 | ||
|
|
f4d9a5f79b | ||
|
|
88ba01b3dd | ||
|
|
d8126e8e30 | ||
|
|
a97ac1f30a | ||
|
|
3b4fd643de | ||
|
|
3ca253537b | ||
|
|
41f527aa46 | ||
|
|
6b932e35bf | ||
|
|
a0cbee8817 | ||
|
|
7e51404ae5 | ||
|
|
618d042001 | ||
|
|
24547b4332 | ||
|
|
ea179a0ee6 | ||
|
|
51ca9f620a | ||
|
|
5bba004324 | ||
|
|
0ffed81601 | ||
|
|
27242d0b66 | ||
|
|
8f994ab632 | ||
|
|
9a56c8fba3 | ||
|
|
c42a44abb6 | ||
|
|
3c8add4b82 | ||
|
|
dfb6cb95d0 | ||
|
|
e22aa91571 | ||
|
|
dd179bc951 | ||
|
|
8934cb6b1c | ||
|
|
e09ec08881 | ||
|
|
e37213ac01 |
@@ -1 +1,3 @@
|
||||
# The extended (may be experimental) features outside the main gost tree.
|
||||
# The extended (may be experimental) features outside the main gost tree.
|
||||
|
||||
|
||||
|
||||
+47
-18
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/internal/loader"
|
||||
"github.com/go-gost/x/internal/matcher"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
@@ -75,6 +76,7 @@ type localAdmission struct {
|
||||
mu sync.RWMutex
|
||||
cancelFunc context.CancelFunc
|
||||
options options
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewAdmission creates and initializes a new Admission using matcher patterns as its match rules.
|
||||
@@ -85,18 +87,19 @@ func NewAdmission(opts ...Option) admission.Admission {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
p := &localAdmission{
|
||||
cancelFunc: cancel,
|
||||
options: options,
|
||||
ipMatcher: matcher.NopMatcher(),
|
||||
cidrMatcher: matcher.NopMatcher(),
|
||||
cancelFunc: cancel,
|
||||
options: options,
|
||||
logger: options.logger,
|
||||
}
|
||||
if p.logger == nil {
|
||||
p.logger = xlogger.Nop()
|
||||
}
|
||||
|
||||
if err := p.reload(ctx); err != nil {
|
||||
options.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
if p.options.period > 0 {
|
||||
go p.periodReload(ctx)
|
||||
}
|
||||
go p.periodReload(ctx)
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -117,13 +120,20 @@ func (p *localAdmission) Admit(ctx context.Context, addr string, opts ...admissi
|
||||
p.options.whitelist && matched
|
||||
|
||||
if !b {
|
||||
p.options.logger.Debugf("%s is denied", addr)
|
||||
p.logger.Debugf("%s is denied", addr)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (p *localAdmission) periodReload(ctx context.Context) error {
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
|
||||
period := p.options.period
|
||||
if period <= 0 {
|
||||
return nil
|
||||
}
|
||||
if period < time.Second {
|
||||
period = time.Second
|
||||
}
|
||||
@@ -134,7 +144,7 @@ func (p *localAdmission) periodReload(ctx context.Context) error {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.options.logger.Warnf("reload: %v", err)
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
// return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
@@ -162,7 +172,7 @@ func (p *localAdmission) reload(ctx context.Context) error {
|
||||
continue
|
||||
}
|
||||
if ipAddr, _ := net.ResolveIPAddr("ip", pattern); ipAddr != nil {
|
||||
p.options.logger.Debugf("resolve IP: %s -> %s", pattern, ipAddr)
|
||||
p.logger.Debugf("resolve IP: %s -> %s", pattern, ipAddr)
|
||||
ips = append(ips, ipAddr.IP)
|
||||
}
|
||||
}
|
||||
@@ -181,7 +191,7 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
|
||||
if lister, ok := p.options.fileLoader.(loader.Lister); ok {
|
||||
list, er := lister.List(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
for _, s := range list {
|
||||
if line := p.parseLine(s); line != "" {
|
||||
@@ -191,7 +201,7 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
|
||||
} else {
|
||||
r, er := p.options.fileLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
@@ -202,13 +212,13 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
|
||||
if lister, ok := p.options.redisLoader.(loader.Lister); ok {
|
||||
list, er := lister.List(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("redis loader: %v", er)
|
||||
p.logger.Warnf("redis loader: %v", er)
|
||||
}
|
||||
patterns = append(patterns, list...)
|
||||
} else {
|
||||
r, er := p.options.redisLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("redis loader: %v", er)
|
||||
p.logger.Warnf("redis loader: %v", er)
|
||||
}
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
@@ -219,14 +229,14 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
|
||||
if p.options.httpLoader != nil {
|
||||
r, er := p.options.httpLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("http loader: %v", er)
|
||||
p.logger.Warnf("http loader: %v", er)
|
||||
}
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
}
|
||||
}
|
||||
|
||||
p.options.logger.Debugf("load items %d", len(patterns))
|
||||
p.logger.Debugf("load items %d", len(patterns))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -271,3 +281,22 @@ func (p *localAdmission) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type admissionGroup struct {
|
||||
admissions []admission.Admission
|
||||
}
|
||||
|
||||
func AdmissionGroup(admissions ...admission.Admission) admission.Admission {
|
||||
return &admissionGroup{
|
||||
admissions: admissions,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *admissionGroup) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
|
||||
for _, admission := range p.admissions {
|
||||
if admission != nil && !admission.Admit(ctx, addr, opts...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -48,9 +48,15 @@ func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.O
|
||||
return false
|
||||
}
|
||||
|
||||
var options admission.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
r, err := p.client.Admit(ctx,
|
||||
&proto.AdmissionRequest{
|
||||
Addr: addr,
|
||||
Service: options.Service,
|
||||
Addr: addr,
|
||||
})
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
)
|
||||
|
||||
type httpPluginRequest struct {
|
||||
Addr string `json:"addr"`
|
||||
Service string `json:"service"`
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
type httpPluginResponse struct {
|
||||
@@ -49,8 +50,14 @@ func (p *httpPlugin) Admit(ctx context.Context, addr string, opts ...admission.O
|
||||
return
|
||||
}
|
||||
|
||||
var options admission.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
rb := httpPluginRequest{
|
||||
Addr: addr,
|
||||
Service: options.Service,
|
||||
Addr: addr,
|
||||
}
|
||||
v, err := json.Marshal(&rb)
|
||||
if err != nil {
|
||||
|
||||
+25
-10
@@ -8,7 +8,8 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/go-gost/core/admission"
|
||||
"github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/x/ctx"
|
||||
xio "github.com/go-gost/x/internal/io"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
"github.com/go-gost/x/internal/net/udp"
|
||||
)
|
||||
@@ -50,9 +51,23 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *serverConn) Metadata() metadata.Metadata {
|
||||
if md, ok := c.Conn.(metadata.Metadatable); ok {
|
||||
return md.Metadata()
|
||||
func (c *serverConn) CloseRead() error {
|
||||
if sc, ok := c.Conn.(xio.CloseRead); ok {
|
||||
return sc.CloseRead()
|
||||
}
|
||||
return xio.ErrUnsupported
|
||||
}
|
||||
|
||||
func (c *serverConn) CloseWrite() error {
|
||||
if sc, ok := c.Conn.(xio.CloseWrite); ok {
|
||||
return sc.CloseWrite()
|
||||
}
|
||||
return xio.ErrUnsupported
|
||||
}
|
||||
|
||||
func (c *serverConn) Context() context.Context {
|
||||
if innerCtx, ok := c.Conn.(ctx.Context); ok {
|
||||
return innerCtx.Context()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -88,9 +103,9 @@ func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *packetConn) Metadata() metadata.Metadata {
|
||||
if md, ok := c.PacketConn.(metadata.Metadatable); ok {
|
||||
return md.Metadata()
|
||||
func (c *packetConn) Context() context.Context {
|
||||
if innerCtx, ok := c.PacketConn.(ctx.Context); ok {
|
||||
return innerCtx.Context()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -234,9 +249,9 @@ func (c *udpConn) SetDSCP(n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *udpConn) Metadata() metadata.Metadata {
|
||||
if md, ok := c.PacketConn.(metadata.Metadatable); ok {
|
||||
return md.Metadata()
|
||||
func (c *udpConn) Context() context.Context {
|
||||
if innerCtx, ok := c.PacketConn.(ctx.Context); ok {
|
||||
return innerCtx.Context()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,19 +6,22 @@ import (
|
||||
|
||||
"github.com/go-gost/core/admission"
|
||||
"github.com/go-gost/core/logger"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
)
|
||||
|
||||
type listener struct {
|
||||
service string
|
||||
net.Listener
|
||||
admission admission.Admission
|
||||
log logger.Logger
|
||||
}
|
||||
|
||||
func WrapListener(admission admission.Admission, ln net.Listener) net.Listener {
|
||||
func WrapListener(service string, admission admission.Admission, ln net.Listener) net.Listener {
|
||||
if admission == nil {
|
||||
return ln
|
||||
}
|
||||
return &listener{
|
||||
service: service,
|
||||
Listener: ln,
|
||||
admission: admission,
|
||||
}
|
||||
@@ -30,8 +33,21 @@ func (ln *listener) Accept() (net.Conn, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if cc, ok := c.(xctx.Context); ok {
|
||||
if cv := cc.Context(); cv != nil {
|
||||
ctx = cv
|
||||
}
|
||||
}
|
||||
|
||||
clientAddr := c.RemoteAddr()
|
||||
if addr := xctx.SrcAddrFromContext(ctx); addr != nil {
|
||||
clientAddr = addr
|
||||
}
|
||||
|
||||
if ln.admission != nil &&
|
||||
!ln.admission.Admit(context.Background(), c.RemoteAddr().String()) {
|
||||
!ln.admission.Admit(ctx, clientAddr.String(), admission.WithService(ln.service)) {
|
||||
c.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
+50
-82
@@ -1,14 +1,14 @@
|
||||
// swagger generate spec -o swagger.yaml
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/service"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -17,56 +17,22 @@ var (
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Msg string `json:"msg,omitempty"`
|
||||
Code int `json:"code,omitempty"`
|
||||
Msg string `json:"msg,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type options struct {
|
||||
accessLog bool
|
||||
pathPrefix string
|
||||
auther auth.Authenticator
|
||||
type Options struct {
|
||||
AccessLog bool
|
||||
PathPrefix string
|
||||
Auther auth.Authenticator
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
func PathPrefixOption(pathPrefix string) Option {
|
||||
return func(o *options) {
|
||||
o.pathPrefix = pathPrefix
|
||||
}
|
||||
}
|
||||
|
||||
func AccessLogOption(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.accessLog = enable
|
||||
}
|
||||
}
|
||||
|
||||
func AutherOption(auther auth.Authenticator) Option {
|
||||
return func(o *options) {
|
||||
o.auther = auther
|
||||
}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
s *http.Server
|
||||
ln net.Listener
|
||||
cclose chan struct{}
|
||||
}
|
||||
|
||||
func NewService(addr string, opts ...Option) (service.Service, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func Register(r *gin.Engine, opts *Options) {
|
||||
if opts == nil {
|
||||
opts = &Options{}
|
||||
}
|
||||
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(
|
||||
cors.New((cors.Config{
|
||||
AllowAllOrigins: true,
|
||||
@@ -76,115 +42,117 @@ func NewService(addr string, opts ...Option) (service.Service, error) {
|
||||
})),
|
||||
gin.Recovery(),
|
||||
)
|
||||
if options.accessLog {
|
||||
if opts.AccessLog {
|
||||
r.Use(mwLogger())
|
||||
}
|
||||
|
||||
router := r.Group("")
|
||||
if options.pathPrefix != "" {
|
||||
router = router.Group(options.pathPrefix)
|
||||
if opts.PathPrefix != "" {
|
||||
router = router.Group(opts.PathPrefix)
|
||||
}
|
||||
|
||||
router.StaticFS("/docs", http.FS(swaggerDoc))
|
||||
|
||||
config := router.Group("/config")
|
||||
config.Use(mwBasicAuth(options.auther))
|
||||
registerConfig(config)
|
||||
config.Use(mwBasicAuth(opts.Auther))
|
||||
|
||||
return &server{
|
||||
s: &http.Server{
|
||||
Handler: r,
|
||||
},
|
||||
ln: ln,
|
||||
cclose: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) Serve() error {
|
||||
return s.s.Serve(s.ln)
|
||||
}
|
||||
|
||||
func (s *server) Addr() net.Addr {
|
||||
return s.ln.Addr()
|
||||
}
|
||||
|
||||
func (s *server) Close() error {
|
||||
return s.s.Close()
|
||||
}
|
||||
|
||||
func (s *server) IsClosed() bool {
|
||||
select {
|
||||
case <-s.cclose:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func registerConfig(config *gin.RouterGroup) {
|
||||
config.GET("", getConfig)
|
||||
config.POST("", saveConfig)
|
||||
|
||||
config.POST("/reload", reloadConfig)
|
||||
|
||||
config.GET("/services", getServiceList)
|
||||
config.GET("/services/:service", getService)
|
||||
config.POST("/services", createService)
|
||||
config.PUT("/services/:service", updateService)
|
||||
config.DELETE("/services/:service", deleteService)
|
||||
|
||||
config.GET("/chains", getChainList)
|
||||
config.GET("/chains/:chain", getChain)
|
||||
config.POST("/chains", createChain)
|
||||
config.PUT("/chains/:chain", updateChain)
|
||||
config.DELETE("/chains/:chain", deleteChain)
|
||||
|
||||
config.GET("/hops", getHopList)
|
||||
config.GET("/hops/:hop", getHop)
|
||||
config.POST("/hops", createHop)
|
||||
config.PUT("/hops/:hop", updateHop)
|
||||
config.DELETE("/hops/:hop", deleteHop)
|
||||
|
||||
config.GET("/authers", getAutherList)
|
||||
config.GET("/authers/:auther", getAuther)
|
||||
config.POST("/authers", createAuther)
|
||||
config.PUT("/authers/:auther", updateAuther)
|
||||
config.DELETE("/authers/:auther", deleteAuther)
|
||||
|
||||
config.GET("/admissions", getAdmissionList)
|
||||
config.GET("/admissions/:admission", getAdmission)
|
||||
config.POST("/admissions", createAdmission)
|
||||
config.PUT("/admissions/:admission", updateAdmission)
|
||||
config.DELETE("/admissions/:admission", deleteAdmission)
|
||||
|
||||
config.GET("/bypasses", getBypassList)
|
||||
config.GET("/bypasses/:bypass", getBypass)
|
||||
config.POST("/bypasses", createBypass)
|
||||
config.PUT("/bypasses/:bypass", updateBypass)
|
||||
config.DELETE("/bypasses/:bypass", deleteBypass)
|
||||
|
||||
config.GET("/resolvers", getResolverList)
|
||||
config.GET("/resolvers/:resolver", getResolver)
|
||||
config.POST("/resolvers", createResolver)
|
||||
config.PUT("/resolvers/:resolver", updateResolver)
|
||||
config.DELETE("/resolvers/:resolver", deleteResolver)
|
||||
|
||||
config.GET("/hosts", getHostsList)
|
||||
config.GET("/hosts/:hosts", getHosts)
|
||||
config.POST("/hosts", createHosts)
|
||||
config.PUT("/hosts/:hosts", updateHosts)
|
||||
config.DELETE("/hosts/:hosts", deleteHosts)
|
||||
|
||||
config.GET("/ingresses", getIngressList)
|
||||
config.GET("/ingresses/:ingress", getIngress)
|
||||
config.POST("/ingresses", createIngress)
|
||||
config.PUT("/ingresses/:ingress", updateIngress)
|
||||
config.DELETE("/ingresses/:ingress", deleteIngress)
|
||||
|
||||
config.GET("/routers", getRouterList)
|
||||
config.GET("/routers/:router", getRouter)
|
||||
config.POST("/routers", createRouter)
|
||||
config.PUT("/routers/:router", updateRouter)
|
||||
config.DELETE("/routers/:router", deleteRouter)
|
||||
|
||||
config.GET("/observers", getObserverList)
|
||||
config.GET("/observers/:observer", getObserver)
|
||||
config.POST("/observers", createObserver)
|
||||
config.PUT("/observers/:observer", updateObserver)
|
||||
config.DELETE("/observers/:observer", deleteObserver)
|
||||
|
||||
config.GET("/recorders", getRecorderList)
|
||||
config.GET("/recorders/:recorder", getRecorder)
|
||||
config.POST("/recorders", createRecorder)
|
||||
config.PUT("/recorders/:recorder", updateRecorder)
|
||||
config.DELETE("/recorders/:recorder", deleteRecorder)
|
||||
|
||||
config.GET("/sds", getSDList)
|
||||
config.GET("/sds/:sd", getSD)
|
||||
config.POST("/sds", createSD)
|
||||
config.PUT("/sds/:sd", updateSD)
|
||||
config.DELETE("/sds/:sd", deleteSD)
|
||||
|
||||
config.GET("/limiters", getLimiterList)
|
||||
config.GET("/limiters/:limiter", getLimiter)
|
||||
config.POST("/limiters", createLimiter)
|
||||
config.PUT("/limiters/:limiter", updateLimiter)
|
||||
config.DELETE("/limiters/:limiter", deleteLimiter)
|
||||
|
||||
config.GET("/climiters", getConnLimiterList)
|
||||
config.GET("/climiters/:limiter", getConnLimiter)
|
||||
config.POST("/climiters", createConnLimiter)
|
||||
config.PUT("/climiters/:limiter", updateConnLimiter)
|
||||
config.DELETE("/climiters/:limiter", deleteConnLimiter)
|
||||
|
||||
config.GET("/rlimiters", getRateLimiterList)
|
||||
config.GET("/rlimiters/:limiter", getRateLimiter)
|
||||
config.POST("/rlimiters", createRateLimiter)
|
||||
config.PUT("/rlimiters/:limiter", updateRateLimiter)
|
||||
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getAdmissionListRequest
|
||||
type getAdmissionListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getAdmissionListResponse
|
||||
type getAdmissionListResponse struct {
|
||||
// in: body
|
||||
Data admissionList
|
||||
}
|
||||
|
||||
type admissionList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.AdmissionConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getAdmissionList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/admissions Admission getAdmissionListRequest
|
||||
//
|
||||
// Get admission list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getAdmissionListResponse
|
||||
|
||||
var req getAdmissionListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Admissions
|
||||
|
||||
var resp getAdmissionListResponse
|
||||
resp.Data = admissionList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getAdmissionRequest
|
||||
type getAdmissionRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Admission string `uri:"admission" json:"admission"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getAdmissionResponse
|
||||
type getAdmissionResponse struct {
|
||||
// in: body
|
||||
Data *config.AdmissionConfig
|
||||
}
|
||||
|
||||
func getAdmission(ctx *gin.Context) {
|
||||
// swagger:route GET /config/admissions/{admission} Admission getAdmissionRequest
|
||||
//
|
||||
// Get admission.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getAdmissionResponse
|
||||
|
||||
var req getAdmissionRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getAdmissionResponse
|
||||
|
||||
for _, admission := range config.Global().Admissions {
|
||||
if admission == nil {
|
||||
continue
|
||||
}
|
||||
if admission.Name == req.Admission {
|
||||
resp.Data = admission
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createAdmissionRequest
|
||||
type createAdmissionRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getAutherListRequest
|
||||
type getAutherListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getAutherListResponse
|
||||
type getAutherListResponse struct {
|
||||
// in: body
|
||||
Data autherList
|
||||
}
|
||||
|
||||
type autherList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.AutherConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getAutherList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/authers Auther getAutherListRequest
|
||||
//
|
||||
// Get auther list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getAutherListResponse
|
||||
|
||||
var req getAutherListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Authers
|
||||
|
||||
var resp getAutherListResponse
|
||||
resp.Data = autherList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getAutherRequest
|
||||
type getAutherRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Auther string `uri:"auther" json:"auther"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getAutherResponse
|
||||
type getAutherResponse struct {
|
||||
// in: body
|
||||
Data *config.AutherConfig
|
||||
}
|
||||
|
||||
func getAuther(ctx *gin.Context) {
|
||||
// swagger:route GET /config/authers/{auther} Auther getAutherRequest
|
||||
//
|
||||
// Get auther.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getAutherResponse
|
||||
|
||||
var req getAutherRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getAutherResponse
|
||||
|
||||
for _, auther := range config.Global().Authers {
|
||||
if auther == nil {
|
||||
continue
|
||||
}
|
||||
if auther.Name == req.Auther {
|
||||
resp.Data = auther
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createAutherRequest
|
||||
type createAutherRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getBypassListRequest
|
||||
type getBypassListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getBypassListResponse
|
||||
type getBypassListResponse struct {
|
||||
// in: body
|
||||
Data bypassList
|
||||
}
|
||||
|
||||
type bypassList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.BypassConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getBypassList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/bypasses Bypass getBypassListRequest
|
||||
//
|
||||
// Get bypass list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getBypassListResponse
|
||||
|
||||
var req getBypassListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Bypasses
|
||||
|
||||
var resp getBypassListResponse
|
||||
resp.Data = bypassList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getBypassRequest
|
||||
type getBypassRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Bypass string `uri:"bypass" json:"bypass"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getBypassResponse
|
||||
type getBypassResponse struct {
|
||||
// in: body
|
||||
Data *config.BypassConfig
|
||||
}
|
||||
|
||||
func getBypass(ctx *gin.Context) {
|
||||
// swagger:route GET /config/bypasses/{bypass} Bypass getBypassRequest
|
||||
//
|
||||
// Get bypass.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getBypassResponse
|
||||
|
||||
var req getBypassRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getBypassResponse
|
||||
|
||||
for _, bypass := range config.Global().Bypasses {
|
||||
if bypass == nil {
|
||||
continue
|
||||
}
|
||||
if bypass.Name == req.Bypass {
|
||||
resp.Data = bypass
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createBypassRequest
|
||||
type createBypassRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -12,6 +12,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getChainListRequest
|
||||
type getChainListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getChainListResponse
|
||||
type getChainListResponse struct {
|
||||
// in: body
|
||||
Data chainList
|
||||
}
|
||||
|
||||
type chainList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.ChainConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getChainList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/chains Chain getChainListRequest
|
||||
//
|
||||
// Get chain list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getChainListResponse
|
||||
|
||||
var req getChainListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Chains
|
||||
|
||||
var resp getChainListResponse
|
||||
resp.Data = chainList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getChainRequest
|
||||
type getChainRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Chain string `uri:"chain" json:"chain"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getChainResponse
|
||||
type getChainResponse struct {
|
||||
// in: body
|
||||
Data *config.ChainConfig
|
||||
}
|
||||
|
||||
func getChain(ctx *gin.Context) {
|
||||
// swagger:route GET /config/chains/{chain} Chain getChainRequest
|
||||
//
|
||||
// Get chain.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getChainResponse
|
||||
|
||||
var req getChainRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getChainResponse
|
||||
|
||||
for _, chain := range config.Global().Chains {
|
||||
if chain == nil {
|
||||
continue
|
||||
}
|
||||
if chain.Name == req.Chain {
|
||||
resp.Data = chain
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createChainRequest
|
||||
type createChainRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getConnLimiterListRequest
|
||||
type getConnLimiterListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getConnLimiterListResponse
|
||||
type getConnLimiterListResponse struct {
|
||||
// in: body
|
||||
Data connLimiterList
|
||||
}
|
||||
|
||||
type connLimiterList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.LimiterConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getConnLimiterList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/climiters Limiter getConnLimiterListRequest
|
||||
//
|
||||
// Get conn limiter list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getConnLimiterListResponse
|
||||
|
||||
var req getConnLimiterListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().CLimiters
|
||||
|
||||
var resp getConnLimiterListResponse
|
||||
resp.Data = connLimiterList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getConnLimiterRequest
|
||||
type getConnLimiterRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Limiter string `uri:"limiter" json:"limiter"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getConnLimiterResponse
|
||||
type getConnLimiterResponse struct {
|
||||
// in: body
|
||||
Data *config.LimiterConfig
|
||||
}
|
||||
|
||||
func getConnLimiter(ctx *gin.Context) {
|
||||
// swagger:route GET /config/climiters/{limiter} Limiter getConnLimiterRequest
|
||||
//
|
||||
// Get conn limiter.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getConnLimiterResponse
|
||||
|
||||
var req getConnLimiterRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getConnLimiterResponse
|
||||
|
||||
for _, limiter := range config.Global().CLimiters{
|
||||
if limiter == nil {
|
||||
continue
|
||||
}
|
||||
if limiter.Name == req.Limiter {
|
||||
resp.Data = limiter
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createConnLimiterRequest
|
||||
type createConnLimiterRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -12,6 +12,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getHopListRequest
|
||||
type getHopListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getHopListResponse
|
||||
type getHopListResponse struct {
|
||||
// in: body
|
||||
Data hopList
|
||||
}
|
||||
|
||||
type hopList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.HopConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getHopList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/hops Hop getHopListRequest
|
||||
//
|
||||
// Get hop list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getHopListResponse
|
||||
|
||||
var req getHopListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Hops
|
||||
|
||||
var resp getHopListResponse
|
||||
resp.Data = hopList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getHopRequest
|
||||
type getHopRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Hop string `uri:"hop" json:"hop"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getHopResponse
|
||||
type getHopResponse struct {
|
||||
// in: body
|
||||
Data *config.HopConfig
|
||||
}
|
||||
|
||||
func getHop(ctx *gin.Context) {
|
||||
// swagger:route GET /config/hops/{hop} Hop getHopRequest
|
||||
//
|
||||
// Get hop.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getHopResponse
|
||||
|
||||
var req getHopRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getHopResponse
|
||||
|
||||
for _, hop := range config.Global().Hops {
|
||||
if hop == nil {
|
||||
continue
|
||||
}
|
||||
if hop.Name == req.Hop {
|
||||
resp.Data = hop
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createHopRequest
|
||||
type createHopRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getHostsListRequest
|
||||
type getHostsListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getHostsListResponse
|
||||
type getHostsListResponse struct {
|
||||
// in: body
|
||||
Data hostsList
|
||||
}
|
||||
|
||||
type hostsList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.HostsConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getHostsList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/hosts Hosts getHostsListRequest
|
||||
//
|
||||
// Get hosts list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getHostsListResponse
|
||||
|
||||
var req getHostsListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Hosts
|
||||
|
||||
var resp getHostsListResponse
|
||||
resp.Data = hostsList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getHostsRequest
|
||||
type getHostsRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Hosts string `uri:"hosts" json:"hosts"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getHostsResponse
|
||||
type getHostsResponse struct {
|
||||
// in: body
|
||||
Data *config.HostsConfig
|
||||
}
|
||||
|
||||
func getHosts(ctx *gin.Context) {
|
||||
// swagger:route GET /config/hosts/{hosts} Hosts getHostsRequest
|
||||
//
|
||||
// Get hosts.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getHostsResponse
|
||||
|
||||
var req getHostsRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getHostsResponse
|
||||
|
||||
for _, hosts := range config.Global().Hosts {
|
||||
if hosts == nil {
|
||||
continue
|
||||
}
|
||||
if hosts.Name == req.Hosts {
|
||||
resp.Data = hosts
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createHostsRequest
|
||||
type createHostsRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getIngressListRequest
|
||||
type getIngressListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getIngressListResponse
|
||||
type getIngressListResponse struct {
|
||||
// in: body
|
||||
Data ingressList
|
||||
}
|
||||
|
||||
type ingressList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.IngressConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getIngressList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/ingresses Ingress getIngressListRequest
|
||||
//
|
||||
// Get ingress list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getIngressListResponse
|
||||
|
||||
var req getIngressListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Ingresses
|
||||
|
||||
var resp getIngressListResponse
|
||||
resp.Data = ingressList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getIngressRequest
|
||||
type getIngressRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Ingress string `uri:"ingress" json:"ingress"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getIngressResponse
|
||||
type getIngressResponse struct {
|
||||
// in: body
|
||||
Data *config.IngressConfig
|
||||
}
|
||||
|
||||
func getIngress(ctx *gin.Context) {
|
||||
// swagger:route GET /config/ingresses/{ingress} Ingress getIngressRequest
|
||||
//
|
||||
// Get ingress.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getIngressResponse
|
||||
|
||||
var req getIngressRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getIngressResponse
|
||||
|
||||
for _, ingress := range config.Global().Ingresses {
|
||||
if ingress == nil {
|
||||
continue
|
||||
}
|
||||
if ingress.Name == req.Ingress {
|
||||
resp.Data = ingress
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createIngressRequest
|
||||
type createIngressRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getLimiterListRequest
|
||||
type getLimiterListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getLimiterListResponse
|
||||
type getLimiterListResponse struct {
|
||||
// in: body
|
||||
Data limiterList
|
||||
}
|
||||
|
||||
type limiterList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.LimiterConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getLimiterList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/limiters Limiter getLimiterListRequest
|
||||
//
|
||||
// Get limiter list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getLimiterListResponse
|
||||
|
||||
var req getLimiterListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Limiters
|
||||
|
||||
var resp getLimiterListResponse
|
||||
resp.Data = limiterList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getLimiterRequest
|
||||
type getLimiterRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Limiter string `uri:"limiter" json:"limiter"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getLimiterResponse
|
||||
type getLimiterResponse struct {
|
||||
// in: body
|
||||
Data *config.LimiterConfig
|
||||
}
|
||||
|
||||
func getLimiter(ctx *gin.Context) {
|
||||
// swagger:route GET /config/limiters/{limiter} Limiter getLimiterRequest
|
||||
//
|
||||
// Get limiter.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getLimiterResponse
|
||||
|
||||
var req getLimiterRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getLimiterResponse
|
||||
|
||||
for _, limiter := range config.Global().Limiters {
|
||||
if limiter == nil {
|
||||
continue
|
||||
}
|
||||
if limiter.Name == req.Limiter {
|
||||
resp.Data = limiter
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createLimiterRequest
|
||||
type createLimiterRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getObserverListRequest
|
||||
type getObserverListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getObserverListResponse
|
||||
type getObserverListResponse struct {
|
||||
// in: body
|
||||
Data observerList
|
||||
}
|
||||
|
||||
type observerList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.ObserverConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getObserverList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/observers Observer getObserverListRequest
|
||||
//
|
||||
// Get observer list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getObserverListResponse
|
||||
|
||||
var req getObserverListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Observers
|
||||
|
||||
var resp getObserverListResponse
|
||||
resp.Data = observerList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getObserverRequest
|
||||
type getObserverRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Observer string `uri:"observer" json:"observer"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getObserverResponse
|
||||
type getObserverResponse struct {
|
||||
// in: body
|
||||
Data *config.ObserverConfig
|
||||
}
|
||||
|
||||
func getObserver(ctx *gin.Context) {
|
||||
// swagger:route GET /config/observers/{observer} Observer getObserverRequest
|
||||
//
|
||||
// Get observer.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getObserverResponse
|
||||
|
||||
var req getObserverRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getObserverResponse
|
||||
|
||||
for _, observer := range config.Global().Observers {
|
||||
if observer == nil {
|
||||
continue
|
||||
}
|
||||
if observer.Name == req.Observer {
|
||||
resp.Data = observer
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createObserverRequest
|
||||
type createObserverRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getRateLimiterListRequest
|
||||
type getRateLimiterListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRateLimiterListResponse
|
||||
type getRateLimiterListResponse struct {
|
||||
// in: body
|
||||
Data rateLimiterList
|
||||
}
|
||||
|
||||
type rateLimiterList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.LimiterConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getRateLimiterList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/rlimiters Limiter getRateLimiterListRequest
|
||||
//
|
||||
// Get rate limiter list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRateLimiterListResponse
|
||||
|
||||
var req getRateLimiterListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().CLimiters
|
||||
|
||||
var resp getRateLimiterListResponse
|
||||
resp.Data = rateLimiterList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getRateLimiterRequest
|
||||
type getRateLimiterRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Limiter string `uri:"limiter" json:"limiter"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRateLimiterResponse
|
||||
type getRateLimiterResponse struct {
|
||||
// in: body
|
||||
Data *config.LimiterConfig
|
||||
}
|
||||
|
||||
func getRateLimiter(ctx *gin.Context) {
|
||||
// swagger:route GET /config/rlimiters/{limiter} Limiter getRateLimiterRequest
|
||||
//
|
||||
// Get conn limiter.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRateLimiterResponse
|
||||
|
||||
var req getRateLimiterRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getRateLimiterResponse
|
||||
|
||||
for _, limiter := range config.Global().CLimiters{
|
||||
if limiter == nil {
|
||||
continue
|
||||
}
|
||||
if limiter.Name == req.Limiter {
|
||||
resp.Data = limiter
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createRateLimiterRequest
|
||||
type createRateLimiterRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getRecorderListRequest
|
||||
type getRecorderListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRecorderListResponse
|
||||
type getRecorderListResponse struct {
|
||||
// in: body
|
||||
Data recorderList
|
||||
}
|
||||
|
||||
type recorderList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.RecorderConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getRecorderList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/recorders Recorder getRecorderListRequest
|
||||
//
|
||||
// Get recorder list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRecorderListResponse
|
||||
|
||||
var req getRecorderListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Recorders
|
||||
|
||||
var resp getRecorderListResponse
|
||||
resp.Data = recorderList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getRecorderRequest
|
||||
type getRecorderRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Recorder string `uri:"recorder" json:"recorder"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRecorderResponse
|
||||
type getRecorderResponse struct {
|
||||
// in: body
|
||||
Data *config.RecorderConfig
|
||||
}
|
||||
|
||||
func getRecorder(ctx *gin.Context) {
|
||||
// swagger:route GET /config/recorders/{recorder} Recorder getRecorderRequest
|
||||
//
|
||||
// Get recorder.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRecorderResponse
|
||||
|
||||
var req getRecorderRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getRecorderResponse
|
||||
|
||||
for _, recorder := range config.Global().Recorders {
|
||||
if recorder == nil {
|
||||
continue
|
||||
}
|
||||
if recorder.Name == req.Recorder {
|
||||
resp.Data = recorder
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createRecorderRequest
|
||||
type createRecorderRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/loader"
|
||||
"github.com/go-gost/x/config/parsing/parser"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters reloadConfigRequest
|
||||
type reloadConfigRequest struct{}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response reloadConfigResponse
|
||||
type reloadConfigResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func reloadConfig(ctx *gin.Context) {
|
||||
// swagger:route POST /config/reload Reload reloadConfigRequest
|
||||
//
|
||||
// Hot reload config.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: reloadConfigResponse
|
||||
|
||||
cfg, err := parser.Parse()
|
||||
if err != nil {
|
||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
config.Set(cfg)
|
||||
|
||||
if err := loader.Load(cfg); err != nil {
|
||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
for _, svc := range registry.ServiceRegistry().GetAll() {
|
||||
svc := svc
|
||||
go func() {
|
||||
svc.Serve()
|
||||
}()
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getResolverListRequest
|
||||
type getResolverListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getResolverListResponse
|
||||
type getResolverListResponse struct {
|
||||
// in: body
|
||||
Data resolverList
|
||||
}
|
||||
|
||||
type resolverList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.ResolverConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getResolverList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/resolvers Resolver getResolverListRequest
|
||||
//
|
||||
// Get resolver list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getResolverListResponse
|
||||
|
||||
var req getResolverListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Resolvers
|
||||
|
||||
var resp getResolverListResponse
|
||||
resp.Data = resolverList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getResolverRequest
|
||||
type getResolverRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Resolver string `uri:"resolver" json:"resolver"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getResolverResponse
|
||||
type getResolverResponse struct {
|
||||
// in: body
|
||||
Data *config.ResolverConfig
|
||||
}
|
||||
|
||||
func getResolver(ctx *gin.Context) {
|
||||
// swagger:route GET /config/resolvers/{resolver} Resolver getResolverRequest
|
||||
//
|
||||
// Get resolver.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getResolverResponse
|
||||
|
||||
var req getResolverRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getResolverResponse
|
||||
|
||||
for _, resolver := range config.Global().Resolvers {
|
||||
if resolver == nil {
|
||||
continue
|
||||
}
|
||||
if resolver.Name == req.Resolver {
|
||||
resp.Data = resolver
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createResolverRequest
|
||||
type createResolverRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getRouterListRequest
|
||||
type getRouterListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRouterListResponse
|
||||
type getRouterListResponse struct {
|
||||
// in: body
|
||||
Data routerList
|
||||
}
|
||||
|
||||
type routerList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.RouterConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getRouterList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/routers Router getRouterListRequest
|
||||
//
|
||||
// Get router list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRouterListResponse
|
||||
|
||||
var req getRouterListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Routers
|
||||
|
||||
var resp getRouterListResponse
|
||||
resp.Data = routerList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getRouterRequest
|
||||
type getRouterRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Router string `uri:"router" json:"router"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getRouterResponse
|
||||
type getRouterResponse struct {
|
||||
// in: body
|
||||
Data *config.RouterConfig
|
||||
}
|
||||
|
||||
func getRouter(ctx *gin.Context) {
|
||||
// swagger:route GET /config/routers/{router} Router getRouterRequest
|
||||
//
|
||||
// Get router.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getRouterResponse
|
||||
|
||||
var req getRouterRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getRouterResponse
|
||||
|
||||
for _, router := range config.Global().Routers {
|
||||
if router == nil {
|
||||
continue
|
||||
}
|
||||
if router.Name == req.Router {
|
||||
resp.Data = router
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createRouterRequest
|
||||
type createRouterRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getSDListRequest
|
||||
type getSDListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getSDListResponse
|
||||
type getSDListResponse struct {
|
||||
// in: body
|
||||
Data sdList
|
||||
}
|
||||
|
||||
type sdList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.SDConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getSDList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/sds SD getSDListRequest
|
||||
//
|
||||
// Get sd list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getSDListResponse
|
||||
|
||||
var req getSDListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().SDs
|
||||
|
||||
var resp getSDListResponse
|
||||
resp.Data = sdList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getSDRequest
|
||||
type getSDRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
SD string `uri:"sd" json:"sd"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getSDResponse
|
||||
type getSDResponse struct {
|
||||
// in: body
|
||||
Data *config.SDConfig
|
||||
}
|
||||
|
||||
func getSD(ctx *gin.Context) {
|
||||
// swagger:route GET /config/sds/{sd} SD getSDRequest
|
||||
//
|
||||
// Get sd.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getSDResponse
|
||||
|
||||
var req getSDRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getSDResponse
|
||||
|
||||
for _, sd := range config.Global().SDs {
|
||||
if sd == nil {
|
||||
continue
|
||||
}
|
||||
if sd.Name == req.SD {
|
||||
resp.Data = sd
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createSDRequest
|
||||
type createSDRequest struct {
|
||||
// in: body
|
||||
|
||||
@@ -11,6 +11,93 @@ import (
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters getServiceListRequest
|
||||
type getServiceListRequest struct {
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getServiceListResponse
|
||||
type getServiceListResponse struct {
|
||||
// in: body
|
||||
Data serviceList
|
||||
}
|
||||
|
||||
type serviceList struct {
|
||||
Count int `json:"count"`
|
||||
List []*config.ServiceConfig `json:"list"`
|
||||
}
|
||||
|
||||
func getServiceList(ctx *gin.Context) {
|
||||
// swagger:route GET /config/services Service getServiceListRequest
|
||||
//
|
||||
// Get service list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getServiceListResponse
|
||||
|
||||
var req getServiceListRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
list := config.Global().Services
|
||||
|
||||
var resp getServiceListResponse
|
||||
resp.Data = serviceList{
|
||||
Count: len(list),
|
||||
List: list,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters getServiceRequest
|
||||
type getServiceRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Service string `uri:"service" json:"service"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getServiceResponse
|
||||
type getServiceResponse struct {
|
||||
// in: body
|
||||
Data *config.ServiceConfig
|
||||
}
|
||||
|
||||
func getService(ctx *gin.Context) {
|
||||
// swagger:route GET /config/services/{service} Service getServiceRequest
|
||||
//
|
||||
// Get service.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getServiceResponse
|
||||
|
||||
var req getServiceRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
var resp getServiceResponse
|
||||
|
||||
for _, service := range config.Global().Services {
|
||||
if service == nil {
|
||||
continue
|
||||
}
|
||||
if service.Name == req.Service {
|
||||
resp.Data = service
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Data: resp.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters createServiceRequest
|
||||
type createServiceRequest struct {
|
||||
// in: body
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
u, p, _ := c.Request.BasicAuth()
|
||||
if _, ok := auther.Authenticate(c, u, p); !ok {
|
||||
if _, ok := auther.Authenticate(c, u, p, auth.WithService("@api")); !ok {
|
||||
c.Writer.Header().Set("WWW-Authenticate", "Basic")
|
||||
c.JSON(http.StatusUnauthorized, Response{
|
||||
Code: http.StatusUnauthorized,
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/service"
|
||||
"github.com/go-gost/x/api"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
accessLog bool
|
||||
pathPrefix string
|
||||
auther auth.Authenticator
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
func PathPrefixOption(pathPrefix string) Option {
|
||||
return func(o *options) {
|
||||
o.pathPrefix = pathPrefix
|
||||
}
|
||||
}
|
||||
|
||||
func AccessLogOption(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.accessLog = enable
|
||||
}
|
||||
}
|
||||
|
||||
func AutherOption(auther auth.Authenticator) Option {
|
||||
return func(o *options) {
|
||||
o.auther = auther
|
||||
}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
s *http.Server
|
||||
ln net.Listener
|
||||
cclose chan struct{}
|
||||
}
|
||||
|
||||
func NewService(network, addr string, opts ...Option) (service.Service, error) {
|
||||
if network == "" {
|
||||
network = "tcp"
|
||||
}
|
||||
ln, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
r := gin.New()
|
||||
api.Register(r, &api.Options{
|
||||
AccessLog: options.accessLog,
|
||||
PathPrefix: options.pathPrefix,
|
||||
Auther: options.auther,
|
||||
})
|
||||
|
||||
return &server{
|
||||
s: &http.Server{
|
||||
Handler: r,
|
||||
},
|
||||
ln: ln,
|
||||
cclose: make(chan struct{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) Serve() error {
|
||||
return s.s.Serve(s.ln)
|
||||
}
|
||||
|
||||
func (s *server) Addr() net.Addr {
|
||||
return s.ln.Addr()
|
||||
}
|
||||
|
||||
func (s *server) Close() error {
|
||||
return s.s.Close()
|
||||
}
|
||||
|
||||
func (s *server) IsClosed() bool {
|
||||
select {
|
||||
case <-s.cclose:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
+898
-8
File diff suppressed because it is too large
Load Diff
+47
-17
@@ -67,6 +67,7 @@ type authenticator struct {
|
||||
mu sync.RWMutex
|
||||
cancelFunc context.CancelFunc
|
||||
options options
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewAuthenticator creates an Authenticator that authenticates client by pre-defined user mapping.
|
||||
@@ -75,23 +76,19 @@ func NewAuthenticator(opts ...Option) auth.Authenticator {
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
if options.logger == nil {
|
||||
options.logger = xlogger.Nop()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
p := &authenticator{
|
||||
kvs: make(map[string]string),
|
||||
cancelFunc: cancel,
|
||||
options: options,
|
||||
logger: options.logger,
|
||||
}
|
||||
if p.logger == nil {
|
||||
p.logger = xlogger.Nop()
|
||||
}
|
||||
|
||||
if err := p.reload(ctx); err != nil {
|
||||
options.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
if p.options.period > 0 {
|
||||
go p.periodReload(ctx)
|
||||
}
|
||||
go p.periodReload(ctx)
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -114,7 +111,14 @@ func (p *authenticator) Authenticate(ctx context.Context, user, password string,
|
||||
}
|
||||
|
||||
func (p *authenticator) periodReload(ctx context.Context) error {
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
|
||||
period := p.options.period
|
||||
if period <= 0 {
|
||||
return nil
|
||||
}
|
||||
if period < time.Second {
|
||||
period = time.Second
|
||||
}
|
||||
@@ -125,7 +129,7 @@ func (p *authenticator) periodReload(ctx context.Context) error {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.options.logger.Warnf("reload: %v", err)
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
// return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
@@ -145,7 +149,7 @@ func (p *authenticator) reload(ctx context.Context) (err error) {
|
||||
kvs[k] = v
|
||||
}
|
||||
|
||||
p.options.logger.Debugf("load items %d", len(m))
|
||||
p.logger.Debugf("load items %d", len(m))
|
||||
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
@@ -162,13 +166,13 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
|
||||
if mapper, ok := p.options.fileLoader.(loader.Mapper); ok {
|
||||
auths, er := mapper.Map(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
m = auths
|
||||
} else {
|
||||
r, er := p.options.fileLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
if auths, _ := p.parseAuths(r); auths != nil {
|
||||
m = auths
|
||||
@@ -179,7 +183,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
|
||||
if mapper, ok := p.options.fileLoader.(loader.Mapper); ok {
|
||||
auths, er := mapper.Map(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
for k, v := range auths {
|
||||
m[k] = v
|
||||
@@ -187,7 +191,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
|
||||
} else {
|
||||
r, er := p.options.redisLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("redis loader: %v", er)
|
||||
p.logger.Warnf("redis loader: %v", er)
|
||||
}
|
||||
if auths, _ := p.parseAuths(r); auths != nil {
|
||||
for k, v := range auths {
|
||||
@@ -199,7 +203,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
|
||||
if p.options.httpLoader != nil {
|
||||
r, er := p.options.httpLoader.Load(ctx)
|
||||
if er != nil {
|
||||
p.options.logger.Warnf("http loader: %v", er)
|
||||
p.logger.Warnf("http loader: %v", er)
|
||||
}
|
||||
if auths, _ := p.parseAuths(r); auths != nil {
|
||||
for k, v := range auths {
|
||||
@@ -252,3 +256,29 @@ func (p *authenticator) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type authenticatorGroup struct {
|
||||
authers []auth.Authenticator
|
||||
}
|
||||
|
||||
func AuthenticatorGroup(authers ...auth.Authenticator) auth.Authenticator {
|
||||
return &authenticatorGroup{
|
||||
authers: authers,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *authenticatorGroup) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
|
||||
if len(p.authers) == 0 {
|
||||
return "", false
|
||||
}
|
||||
for _, auther := range p.authers {
|
||||
if auther == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if id, ok := auther.Authenticate(ctx, user, password, opts...); ok {
|
||||
return id, ok
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
+12
-2
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/plugin/auth/proto"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/plugin"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@@ -51,11 +51,21 @@ func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, op
|
||||
return "", false
|
||||
}
|
||||
|
||||
var options auth.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
var clientAddr string
|
||||
if v := xctx.SrcAddrFromContext(ctx); v != nil {
|
||||
clientAddr = v.String()
|
||||
}
|
||||
r, err := p.client.Authenticate(ctx,
|
||||
&proto.AuthenticateRequest{
|
||||
Service: options.Service,
|
||||
Username: user,
|
||||
Password: password,
|
||||
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
|
||||
Client: clientAddr,
|
||||
})
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
|
||||
+14
-2
@@ -8,11 +8,12 @@ import (
|
||||
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/logger"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/plugin"
|
||||
)
|
||||
|
||||
type httpPluginRequest struct {
|
||||
Service string `json:"service"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Client string `json:"client"`
|
||||
@@ -53,10 +54,21 @@ func (p *httpPlugin) Authenticate(ctx context.Context, user, password string, op
|
||||
return
|
||||
}
|
||||
|
||||
var options auth.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
var clientAddr string
|
||||
if v := xctx.SrcAddrFromContext(ctx); v != nil {
|
||||
clientAddr = v.String()
|
||||
}
|
||||
|
||||
rb := httpPluginRequest{
|
||||
Service: options.Service,
|
||||
Username: user,
|
||||
Password: password,
|
||||
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
|
||||
Client: clientAddr,
|
||||
}
|
||||
v, err := json.Marshal(&rb)
|
||||
if err != nil {
|
||||
|
||||
+148
-67
@@ -6,14 +6,19 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/logger"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/loader"
|
||||
"github.com/go-gost/x/internal/matcher"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -78,9 +83,11 @@ type localBypass struct {
|
||||
cidrMatcher matcher.Matcher
|
||||
addrMatcher matcher.Matcher
|
||||
wildcardMatcher matcher.Matcher
|
||||
cancelFunc context.CancelFunc
|
||||
ipRangeMatcher matcher.Matcher
|
||||
options options
|
||||
logger logger.Logger
|
||||
mu sync.RWMutex
|
||||
cancelFunc context.CancelFunc
|
||||
}
|
||||
|
||||
// NewBypass creates and initializes a new Bypass.
|
||||
@@ -91,36 +98,47 @@ func NewBypass(opts ...Option) bypass.Bypass {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
bp := &localBypass{
|
||||
cancelFunc: cancel,
|
||||
options: options,
|
||||
p := &localBypass{
|
||||
cidrMatcher: matcher.NopMatcher(),
|
||||
addrMatcher: matcher.NopMatcher(),
|
||||
wildcardMatcher: matcher.NopMatcher(),
|
||||
ipRangeMatcher: matcher.NopMatcher(),
|
||||
cancelFunc: cancel,
|
||||
options: options,
|
||||
logger: options.logger,
|
||||
}
|
||||
if p.logger == nil {
|
||||
p.logger = xlogger.Nop()
|
||||
}
|
||||
|
||||
if err := bp.reload(ctx); err != nil {
|
||||
options.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
if bp.options.period > 0 {
|
||||
go bp.periodReload(ctx)
|
||||
}
|
||||
go p.periodReload(ctx)
|
||||
|
||||
return bp
|
||||
return p
|
||||
}
|
||||
|
||||
func (bp *localBypass) periodReload(ctx context.Context) error {
|
||||
period := bp.options.period
|
||||
func (p *localBypass) periodReload(ctx context.Context) error {
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
}
|
||||
|
||||
period := p.options.period
|
||||
if period <= 0 {
|
||||
return nil
|
||||
}
|
||||
if period < time.Second {
|
||||
period = time.Second
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(period)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if err := bp.reload(ctx); err != nil {
|
||||
bp.options.logger.Warnf("reload: %v", err)
|
||||
if err := p.reload(ctx); err != nil {
|
||||
p.logger.Warnf("reload: %v", err)
|
||||
// return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
@@ -129,84 +147,96 @@ func (bp *localBypass) periodReload(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (bp *localBypass) reload(ctx context.Context) error {
|
||||
v, err := bp.load(ctx)
|
||||
func (p *localBypass) reload(ctx context.Context) error {
|
||||
v, err := p.load(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patterns := append(bp.options.matchers, v...)
|
||||
bp.options.logger.Debugf("load items %d", len(patterns))
|
||||
patterns := append(p.options.matchers, v...)
|
||||
p.logger.Debugf("load items %d", len(patterns))
|
||||
|
||||
var addrs []string
|
||||
var inets []*net.IPNet
|
||||
var wildcards []string
|
||||
var ipRanges []xnet.IPRange
|
||||
for _, pattern := range patterns {
|
||||
if _, inet, err := net.ParseCIDR(pattern); err == nil {
|
||||
inets = append(inets, inet)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.ContainsAny(pattern, "*?") {
|
||||
wildcards = append(wildcards, pattern)
|
||||
if _, err := glob.Compile(pattern); err == nil {
|
||||
wildcards = append(wildcards, pattern)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
r := xnet.IPRange{}
|
||||
if err := r.Parse(pattern); err == nil {
|
||||
ipRanges = append(ipRanges, r)
|
||||
continue
|
||||
}
|
||||
|
||||
addrs = append(addrs, pattern)
|
||||
}
|
||||
|
||||
bp.mu.Lock()
|
||||
defer bp.mu.Unlock()
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
bp.cidrMatcher = matcher.CIDRMatcher(inets)
|
||||
bp.addrMatcher = matcher.AddrMatcher(addrs)
|
||||
bp.wildcardMatcher = matcher.WildcardMatcher(wildcards)
|
||||
p.cidrMatcher = matcher.CIDRMatcher(inets)
|
||||
p.addrMatcher = matcher.AddrMatcher(addrs)
|
||||
p.wildcardMatcher = matcher.WildcardMatcher(wildcards)
|
||||
p.ipRangeMatcher = matcher.IPRangeMatcher(ipRanges)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *localBypass) load(ctx context.Context) (patterns []string, err error) {
|
||||
if bp.options.fileLoader != nil {
|
||||
if lister, ok := bp.options.fileLoader.(loader.Lister); ok {
|
||||
func (p *localBypass) load(ctx context.Context) (patterns []string, err error) {
|
||||
if p.options.fileLoader != nil {
|
||||
if lister, ok := p.options.fileLoader.(loader.Lister); ok {
|
||||
list, er := lister.List(ctx)
|
||||
if er != nil {
|
||||
bp.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
for _, s := range list {
|
||||
if line := bp.parseLine(s); line != "" {
|
||||
if line := p.parseLine(s); line != "" {
|
||||
patterns = append(patterns, line)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r, er := bp.options.fileLoader.Load(ctx)
|
||||
r, er := p.options.fileLoader.Load(ctx)
|
||||
if er != nil {
|
||||
bp.options.logger.Warnf("file loader: %v", er)
|
||||
p.logger.Warnf("file loader: %v", er)
|
||||
}
|
||||
if v, _ := bp.parsePatterns(r); v != nil {
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if bp.options.redisLoader != nil {
|
||||
if lister, ok := bp.options.redisLoader.(loader.Lister); ok {
|
||||
if p.options.redisLoader != nil {
|
||||
if lister, ok := p.options.redisLoader.(loader.Lister); ok {
|
||||
list, er := lister.List(ctx)
|
||||
if er != nil {
|
||||
bp.options.logger.Warnf("redis loader: %v", er)
|
||||
p.logger.Warnf("redis loader: %v", er)
|
||||
}
|
||||
patterns = append(patterns, list...)
|
||||
} else {
|
||||
r, er := bp.options.redisLoader.Load(ctx)
|
||||
r, er := p.options.redisLoader.Load(ctx)
|
||||
if er != nil {
|
||||
bp.options.logger.Warnf("redis loader: %v", er)
|
||||
p.logger.Warnf("redis loader: %v", er)
|
||||
}
|
||||
if v, _ := bp.parsePatterns(r); v != nil {
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if bp.options.httpLoader != nil {
|
||||
r, er := bp.options.httpLoader.Load(ctx)
|
||||
if p.options.httpLoader != nil {
|
||||
r, er := p.options.httpLoader.Load(ctx)
|
||||
if er != nil {
|
||||
bp.options.logger.Warnf("http loader: %v", er)
|
||||
p.logger.Warnf("http loader: %v", er)
|
||||
}
|
||||
if v, _ := bp.parsePatterns(r); v != nil {
|
||||
if v, _ := p.parsePatterns(r); v != nil {
|
||||
patterns = append(patterns, v...)
|
||||
}
|
||||
}
|
||||
@@ -214,14 +244,14 @@ func (bp *localBypass) load(ctx context.Context) (patterns []string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func (bp *localBypass) parsePatterns(r io.Reader) (patterns []string, err error) {
|
||||
func (p *localBypass) parsePatterns(r io.Reader) (patterns []string, err error) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
if line := bp.parseLine(scanner.Text()); line != "" {
|
||||
if line := p.parseLine(scanner.Text()); line != "" {
|
||||
patterns = append(patterns, line)
|
||||
}
|
||||
}
|
||||
@@ -230,19 +260,24 @@ func (bp *localBypass) parsePatterns(r io.Reader) (patterns []string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func (bp *localBypass) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
||||
if addr == "" || bp == nil {
|
||||
func (p *localBypass) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
||||
if addr == "" || p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
matched := bp.matched(addr)
|
||||
matched := p.matched(addr)
|
||||
|
||||
b := !p.options.whitelist && matched ||
|
||||
p.options.whitelist && !matched
|
||||
|
||||
log := p.logger.WithFields(map[string]any{
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
b := !bp.options.whitelist && matched ||
|
||||
bp.options.whitelist && !matched
|
||||
if b {
|
||||
bp.options.logger.Debugf("bypass: %s, whitelist: %t", addr, bp.options.whitelist)
|
||||
log.Debugf("bypass: %s, whitelist: %t", addr, p.options.whitelist)
|
||||
} else {
|
||||
bp.options.logger.Debugf("pass: %s, whitelist: %t", addr, bp.options.whitelist)
|
||||
log.Debugf("pass: %s, whitelist: %t", addr, p.options.whitelist)
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -251,18 +286,22 @@ func (p *localBypass) IsWhitelist() bool {
|
||||
return p.options.whitelist
|
||||
}
|
||||
|
||||
func (bp *localBypass) parseLine(s string) string {
|
||||
func (p *localBypass) parseLine(s string) string {
|
||||
if n := strings.IndexByte(s, '#'); n >= 0 {
|
||||
s = s[:n]
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
func (bp *localBypass) matched(addr string) bool {
|
||||
bp.mu.RLock()
|
||||
defer bp.mu.RUnlock()
|
||||
func (p *localBypass) matched(addr string) bool {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if bp.addrMatcher.Match(addr) {
|
||||
if p.ipRangeMatcher.Match(addr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if p.addrMatcher.Match(addr) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -271,20 +310,62 @@ func (bp *localBypass) matched(addr string) bool {
|
||||
host = addr
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return bp.cidrMatcher.Match(host)
|
||||
if ip := net.ParseIP(host); ip != nil && p.cidrMatcher.Match(host) {
|
||||
return true
|
||||
}
|
||||
|
||||
return bp.wildcardMatcher.Match(addr)
|
||||
return p.wildcardMatcher.Match(addr)
|
||||
}
|
||||
|
||||
func (bp *localBypass) Close() error {
|
||||
bp.cancelFunc()
|
||||
if bp.options.fileLoader != nil {
|
||||
bp.options.fileLoader.Close()
|
||||
func (p *localBypass) Close() error {
|
||||
p.cancelFunc()
|
||||
if p.options.fileLoader != nil {
|
||||
p.options.fileLoader.Close()
|
||||
}
|
||||
if bp.options.redisLoader != nil {
|
||||
bp.options.redisLoader.Close()
|
||||
if p.options.redisLoader != nil {
|
||||
p.options.redisLoader.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type bypassGroup struct {
|
||||
bypasses []bypass.Bypass
|
||||
}
|
||||
|
||||
func BypassGroup(bypasses ...bypass.Bypass) bypass.Bypass {
|
||||
return &bypassGroup{
|
||||
bypasses: bypasses,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *bypassGroup) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
||||
var whitelist, blacklist []bool
|
||||
for _, bypass := range p.bypasses {
|
||||
result := bypass.Contains(ctx, network, addr, opts...)
|
||||
if bypass.IsWhitelist() {
|
||||
whitelist = append(whitelist, result)
|
||||
} else {
|
||||
blacklist = append(blacklist, result)
|
||||
}
|
||||
}
|
||||
status := false
|
||||
if len(whitelist) > 0 {
|
||||
if slices.Contains(whitelist, false) {
|
||||
status = false
|
||||
} else {
|
||||
status = true
|
||||
}
|
||||
}
|
||||
if !status && len(blacklist) > 0 {
|
||||
if slices.Contains(blacklist, true) {
|
||||
status = true
|
||||
} else {
|
||||
status = false
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (p *bypassGroup) IsWhitelist() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ..
|
||||
|
||||
r, err := p.client.Bypass(ctx,
|
||||
&proto.BypassRequest{
|
||||
Service: options.Service,
|
||||
Network: network,
|
||||
Addr: addr,
|
||||
Client: string(ctxvalue.ClientIDFromContext(ctx)),
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type httpPluginRequest struct {
|
||||
Service string `json:"service"`
|
||||
Network string `json:"network"`
|
||||
Addr string `json:"addr"`
|
||||
Client string `json:"client"`
|
||||
@@ -60,6 +61,7 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
|
||||
}
|
||||
|
||||
rb := httpPluginRequest{
|
||||
Service: options.Service,
|
||||
Network: network,
|
||||
Addr: addr,
|
||||
Client: string(ctxvalue.ClientIDFromContext(ctx)),
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...
|
||||
netd := dialer.Dialer{
|
||||
Interface: options.Interface,
|
||||
Netns: options.Netns,
|
||||
Logger: options.Logger,
|
||||
Log: options.Logger,
|
||||
}
|
||||
if options.SockOpts != nil {
|
||||
netd.Mark = options.SockOpts.Mark
|
||||
|
||||
+20
-10
@@ -10,7 +10,8 @@ import (
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/recorder"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
ictx "github.com/go-gost/x/internal/ctx"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
)
|
||||
|
||||
@@ -50,7 +51,7 @@ func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Co
|
||||
r.record(ctx, recorder.RecorderServiceRouterDialAddress, []byte(host))
|
||||
|
||||
log := r.options.Logger.WithFields(map[string]any{
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
"sid": xctx.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
conn, err = r.dial(ctx, network, address, log)
|
||||
@@ -96,6 +97,11 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
buf := ictx.BufferFromContext(ctx)
|
||||
if buf != nil {
|
||||
buf.Reset()
|
||||
}
|
||||
|
||||
var ipAddr string
|
||||
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log)
|
||||
if err != nil {
|
||||
@@ -103,19 +109,23 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
|
||||
break
|
||||
}
|
||||
|
||||
if buf != nil {
|
||||
buf.Reset()
|
||||
}
|
||||
|
||||
var route chain.Route
|
||||
if r.options.Chain != nil {
|
||||
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||
buf := bytes.Buffer{}
|
||||
for _, node := range routePath(route) {
|
||||
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s", ipAddr)
|
||||
log.Debugf("route(retry=%d) %s", i, buf.String())
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
for _, node := range routePath(route) {
|
||||
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
|
||||
}
|
||||
fmt.Fprintf(buf, "%s", ipAddr)
|
||||
log.Debugf("route(retry=%d) %s", i, buf.String())
|
||||
|
||||
if route == nil {
|
||||
route = DefaultRoute
|
||||
@@ -142,7 +152,7 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...chai
|
||||
}
|
||||
|
||||
log := r.options.Logger.WithFields(map[string]any{
|
||||
"sid": ctxvalue.SidFromContext(ctx),
|
||||
"sid": xctx.SidFromContext(ctx),
|
||||
})
|
||||
|
||||
log.Debugf("bind on %s/%s", address, network)
|
||||
|
||||
+214
-57
@@ -10,12 +10,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
"github.com/go-gost/x/config"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
"github.com/go-gost/x/limiter/conn"
|
||||
"github.com/go-gost/x/limiter/traffic"
|
||||
mdx "github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -42,24 +42,6 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeConfig, err := buildNodeConfig(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
|
||||
|
||||
var nodes []*config.NodeConfig
|
||||
for _, host := range strings.Split(nodeConfig.Addr, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
nodeCfg := &config.NodeConfig{}
|
||||
*nodeCfg = *nodeConfig
|
||||
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
|
||||
nodeCfg.Addr = host
|
||||
nodes = append(nodes, nodeCfg)
|
||||
}
|
||||
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
@@ -71,8 +53,6 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
|
||||
hopConfig := &config.HopConfig{
|
||||
Name: fmt.Sprintf("%shop-%d", namePrefix, i),
|
||||
Selector: parseSelector(m),
|
||||
Nodes: nodes,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
if v := mdutil.GetString(md, "bypass"); v != "" {
|
||||
@@ -135,15 +115,53 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
|
||||
}
|
||||
|
||||
if v := mdutil.GetString(md, "interface"); v != "" {
|
||||
hopConfig.Interface = v
|
||||
m["hop.interface"] = v
|
||||
delete(m, "interface")
|
||||
}
|
||||
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
|
||||
hopConfig.SockOpts = &config.SockOptsConfig{
|
||||
Mark: v,
|
||||
}
|
||||
m["hop.so_mark"] = v
|
||||
delete(m, "so_mark")
|
||||
}
|
||||
if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 {
|
||||
m["hop.proxyProtocol"] = v
|
||||
delete(m, "proxyProtocol")
|
||||
}
|
||||
|
||||
hopMd := map[string]any{}
|
||||
for k, v := range m {
|
||||
if strings.HasPrefix(k, "node.") ||
|
||||
strings.HasPrefix(k, "connector.") ||
|
||||
strings.HasPrefix(k, "dialer.") {
|
||||
continue
|
||||
}
|
||||
if s, found := strings.CutPrefix(k, "hop."); found {
|
||||
hopMd[s] = v
|
||||
delete(m, k)
|
||||
continue
|
||||
}
|
||||
|
||||
hopMd[k] = v
|
||||
}
|
||||
hopConfig.Metadata = hopMd
|
||||
|
||||
nodeConfig, err := buildNodeConfig(url, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
|
||||
|
||||
var nodes []*config.NodeConfig
|
||||
for _, host := range strings.Split(nodeConfig.Addr, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
nodeCfg := &config.NodeConfig{}
|
||||
*nodeCfg = *nodeConfig
|
||||
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
|
||||
nodeCfg.Addr = host
|
||||
nodes = append(nodes, nodeCfg)
|
||||
}
|
||||
hopConfig.Nodes = nodes
|
||||
|
||||
chain.Hops = append(chain.Hops, hopConfig)
|
||||
}
|
||||
@@ -279,7 +297,7 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
|
||||
}
|
||||
if in != "" || out != "" {
|
||||
limiter.Limits = append(limiter.Limits,
|
||||
fmt.Sprintf("%s %s %s", traffic.GlobalLimitKey, in, out))
|
||||
fmt.Sprintf("%s %s %s", traffic.ServiceLimitKey, in, out))
|
||||
}
|
||||
if cin != "" || cout != "" {
|
||||
limiter.Limits = append(limiter.Limits,
|
||||
@@ -358,9 +376,24 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
||||
listener = schemes[1]
|
||||
}
|
||||
|
||||
addrs := xnet.AddrPortRange(url.Host).Addrs()
|
||||
if len(addrs) == 0 {
|
||||
addrs = append(addrs, url.Host)
|
||||
// For path-based protocols (unix socket, serial), use path as address
|
||||
isPathBasedProtocol := listener == "unix" || listener == "serial"
|
||||
|
||||
var addrs []string
|
||||
if isPathBasedProtocol {
|
||||
path := url.EscapedPath()
|
||||
if url.Host != "" {
|
||||
addrs = append(addrs, url.Host+strings.TrimPrefix(path, "/"))
|
||||
} else if path != "" {
|
||||
addrs = append(addrs, path)
|
||||
} else {
|
||||
addrs = append(addrs, url.Host)
|
||||
}
|
||||
} else {
|
||||
addrs = xnet.AddrPortRange(url.Host).Addrs()
|
||||
if len(addrs) == 0 {
|
||||
addrs = append(addrs, url.Host)
|
||||
}
|
||||
}
|
||||
|
||||
var services []*config.ServiceConfig
|
||||
@@ -382,7 +415,8 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
||||
|
||||
var nodes []*config.ForwardNodeConfig
|
||||
// forward mode
|
||||
if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" {
|
||||
// For path-based protocols, path is address not forward target
|
||||
if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" && !isPathBasedProtocol {
|
||||
i := 0
|
||||
for _, addr := range strings.Split(remotes, ",") {
|
||||
addrs := xnet.AddrPortRange(addr).Addrs()
|
||||
@@ -435,10 +469,15 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
||||
|
||||
var auth *config.AuthConfig
|
||||
if url.User != nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
if strings.HasPrefix(url.Scheme, "ss") {
|
||||
auth = decodeSIP002Auth(url)
|
||||
}
|
||||
if auth == nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
|
||||
m := map[string]any{}
|
||||
@@ -479,19 +518,63 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
||||
}
|
||||
|
||||
if v := mdutil.GetString(md, "dns"); v != "" {
|
||||
md.Set("dns", strings.Split(v, ","))
|
||||
m["dns"] = strings.Split(v, ",")
|
||||
}
|
||||
|
||||
if v := mdutil.GetString(md, "interface"); v != "" {
|
||||
m["service.interface"] = v
|
||||
delete(m, "interface")
|
||||
}
|
||||
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
|
||||
m["service.so_mark"] = v
|
||||
delete(m, "so_mark")
|
||||
}
|
||||
if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 {
|
||||
m["service.proxyProtocol"] = v
|
||||
delete(m, "proxyProtocol")
|
||||
}
|
||||
if v := mdutil.GetString(md, "netns"); v != "" {
|
||||
m["service.netns"] = v
|
||||
delete(m, "netns")
|
||||
}
|
||||
if v := mdutil.GetString(md, "netns.out"); v != "" {
|
||||
m["service.netns.out"] = v
|
||||
delete(m, "netns.out")
|
||||
}
|
||||
|
||||
selector := parseSelector(m)
|
||||
|
||||
serviceMd := map[string]any{}
|
||||
handleMd := map[string]any{}
|
||||
listenerMd := map[string]any{}
|
||||
for k, v := range m {
|
||||
if s, found := strings.CutPrefix(k, "service."); found {
|
||||
serviceMd[s] = v
|
||||
continue
|
||||
}
|
||||
if s, found := strings.CutPrefix(k, "handler."); found {
|
||||
handleMd[s] = v
|
||||
continue
|
||||
}
|
||||
if s, found := strings.CutPrefix(k, "listener."); found {
|
||||
listenerMd[s] = v
|
||||
continue
|
||||
}
|
||||
|
||||
handleMd[k] = v
|
||||
listenerMd[k] = v
|
||||
serviceMd[k] = v
|
||||
}
|
||||
|
||||
handlerCfg := &config.HandlerConfig{
|
||||
Type: handler,
|
||||
Auth: auth,
|
||||
Metadata: m,
|
||||
Metadata: handleMd,
|
||||
}
|
||||
listenerCfg := &config.ListenerConfig{
|
||||
Type: listener,
|
||||
TLS: tlsConfig,
|
||||
Metadata: m,
|
||||
Metadata: listenerMd,
|
||||
}
|
||||
if listenerCfg.Type == "ssh" || listenerCfg.Type == "sshd" {
|
||||
handlerCfg.Auth = nil
|
||||
@@ -504,13 +587,13 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
||||
}
|
||||
svc.Handler = handlerCfg
|
||||
svc.Listener = listenerCfg
|
||||
svc.Metadata = m
|
||||
svc.Metadata = serviceMd
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
func buildNodeConfig(url *url.URL, m map[string]any) (*config.NodeConfig, error) {
|
||||
var connector, dialer string
|
||||
schemes := strings.Split(url.Scheme, "+")
|
||||
if len(schemes) == 1 {
|
||||
@@ -522,19 +605,8 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
dialer = schemes[1]
|
||||
}
|
||||
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
m[k] = v[0]
|
||||
}
|
||||
}
|
||||
md := mdx.NewMetadata(m)
|
||||
|
||||
node := &config.NodeConfig{
|
||||
Addr: url.Host,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
if c := registry.ConnectorRegistry().Get(connector); c == nil {
|
||||
connector = "http"
|
||||
}
|
||||
@@ -547,10 +619,15 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
|
||||
var auth *config.AuthConfig
|
||||
if url.User != nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
if strings.HasPrefix(url.Scheme, "ss") {
|
||||
auth = decodeSIP002Auth(url)
|
||||
}
|
||||
if auth == nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
|
||||
if sauth := mdutil.GetString(md, "auth"); sauth != "" && auth == nil {
|
||||
@@ -591,15 +668,59 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
tlsConfig = nil
|
||||
}
|
||||
|
||||
nodeMd := map[string]any{}
|
||||
connectorMd := map[string]any{}
|
||||
dialerMd := map[string]any{}
|
||||
for k, v := range m {
|
||||
if s, found := strings.CutPrefix(k, "node."); found {
|
||||
nodeMd[s] = v
|
||||
continue
|
||||
}
|
||||
if s, found := strings.CutPrefix(k, "connector."); found {
|
||||
connectorMd[s] = v
|
||||
continue
|
||||
}
|
||||
if s, found := strings.CutPrefix(k, "dialer."); found {
|
||||
dialerMd[s] = v
|
||||
continue
|
||||
}
|
||||
|
||||
connectorMd[k] = v
|
||||
dialerMd[k] = v
|
||||
nodeMd[k] = v
|
||||
}
|
||||
|
||||
// For path-based protocols (unix socket, serial), use path as address
|
||||
var nodeAddr string
|
||||
isPathBasedProtocol := dialer == "unix" || dialer == "serial"
|
||||
if isPathBasedProtocol {
|
||||
path := url.EscapedPath()
|
||||
if url.Host != "" {
|
||||
// Two slashes: host + path = relative path
|
||||
nodeAddr = url.Host + strings.TrimPrefix(path, "/")
|
||||
} else if path != "" {
|
||||
// Three slashes: path is absolute (keep leading slash)
|
||||
nodeAddr = path
|
||||
} else {
|
||||
nodeAddr = url.Host
|
||||
}
|
||||
} else {
|
||||
nodeAddr = url.Host
|
||||
}
|
||||
|
||||
node := &config.NodeConfig{
|
||||
Addr: nodeAddr,
|
||||
Metadata: nodeMd,
|
||||
}
|
||||
node.Connector = &config.ConnectorConfig{
|
||||
Type: connector,
|
||||
Auth: auth,
|
||||
Metadata: m,
|
||||
Metadata: connectorMd,
|
||||
}
|
||||
node.Dialer = &config.DialerConfig{
|
||||
Type: dialer,
|
||||
TLS: tlsConfig,
|
||||
Metadata: m,
|
||||
Metadata: dialerMd,
|
||||
}
|
||||
|
||||
if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" {
|
||||
@@ -631,10 +752,46 @@ func Norm(s string) (*url.URL, error) {
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func parseAuthFromCmd(sa string) (*config.AuthConfig, error) {
|
||||
v, err := base64.StdEncoding.DecodeString(sa)
|
||||
func decodeSIP002Auth(u *url.URL) *config.AuthConfig {
|
||||
if u.User == nil {
|
||||
return nil
|
||||
}
|
||||
_, hasPassword := u.User.Password()
|
||||
if hasPassword {
|
||||
return nil
|
||||
}
|
||||
username := u.User.Username()
|
||||
if username == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
decoded, err = base64.StdEncoding.DecodeString(username)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
cs := string(decoded)
|
||||
n := strings.IndexByte(cs, ':')
|
||||
if n < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &config.AuthConfig{
|
||||
Username: cs[:n],
|
||||
Password: cs[n+1:],
|
||||
}
|
||||
}
|
||||
|
||||
func parseAuthFromCmd(sa string) (*config.AuthConfig, error) {
|
||||
v, err := base64.RawURLEncoding.DecodeString(sa)
|
||||
if err != nil {
|
||||
v, err = base64.StdEncoding.DecodeString(sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cs := string(v)
|
||||
n := strings.IndexByte(cs, ':')
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecodeSIP002Auth(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rawURL string
|
||||
wantUsername string
|
||||
wantPassword string
|
||||
wantNil bool
|
||||
}{
|
||||
{
|
||||
name: "SIP002 URL-safe base64 without padding",
|
||||
rawURL: "ss://" + base64.RawURLEncoding.EncodeToString([]byte("aes-256-gcm:password")) + "@server:8388",
|
||||
wantUsername: "aes-256-gcm",
|
||||
wantPassword: "password",
|
||||
},
|
||||
{
|
||||
name: "SIP002 standard base64 with padding",
|
||||
rawURL: "ss://" + base64.StdEncoding.EncodeToString([]byte("aes-256-gcm:pass")) + "@server:8388",
|
||||
wantUsername: "aes-256-gcm",
|
||||
wantPassword: "pass",
|
||||
},
|
||||
{
|
||||
name: "standard format with password (not SIP002)",
|
||||
rawURL: "ss://aes-256-gcm:password@server:8388",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "no userinfo",
|
||||
rawURL: "ss://server:8388",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "invalid base64",
|
||||
rawURL: "ss://not-valid!!!@server:8388",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "base64 without colon in decoded",
|
||||
rawURL: "ss://" + base64.RawURLEncoding.EncodeToString([]byte("nocolon")) + "@server:8388",
|
||||
wantNil: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
u, err := url.Parse(tt.rawURL)
|
||||
if err != nil {
|
||||
t.Fatalf("url.Parse(%q): %v", tt.rawURL, err)
|
||||
}
|
||||
|
||||
got := decodeSIP002Auth(u)
|
||||
if tt.wantNil {
|
||||
if got != nil {
|
||||
t.Errorf("decodeSIP002Auth() = %+v, want nil", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
if got == nil {
|
||||
t.Fatal("decodeSIP002Auth() = nil, want non-nil")
|
||||
}
|
||||
if got.Username != tt.wantUsername {
|
||||
t.Errorf("Username = %q, want %q", got.Username, tt.wantUsername)
|
||||
}
|
||||
if got.Password != tt.wantPassword {
|
||||
t.Errorf("Password = %q, want %q", got.Password, tt.wantPassword)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAuthFromCmd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantUsername string
|
||||
wantPassword string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "StdEncoding with padding",
|
||||
input: base64.StdEncoding.EncodeToString([]byte("user:pass")),
|
||||
wantUsername: "user",
|
||||
wantPassword: "pass",
|
||||
},
|
||||
{
|
||||
name: "RawURLEncoding without padding",
|
||||
input: base64.RawURLEncoding.EncodeToString([]byte("user:pass")),
|
||||
wantUsername: "user",
|
||||
wantPassword: "pass",
|
||||
},
|
||||
{
|
||||
name: "invalid base64",
|
||||
input: "not-valid!!!",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseAuthFromCmd(tt.input)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Error("parseAuthFromCmd() error = nil, want error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("parseAuthFromCmd() error = %v", err)
|
||||
}
|
||||
if got.Username != tt.wantUsername {
|
||||
t.Errorf("Username = %q, want %q", got.Username, tt.wantUsername)
|
||||
}
|
||||
if got.Password != tt.wantPassword {
|
||||
t.Errorf("Password = %q, want %q", got.Password, tt.wantPassword)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildNodeConfigSIP002(t *testing.T) {
|
||||
encoded := base64.RawURLEncoding.EncodeToString([]byte("aes-256-gcm:password"))
|
||||
rawURL := "ss://" + encoded + "@server:8388"
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
t.Fatalf("url.Parse: %v", err)
|
||||
}
|
||||
|
||||
node, err := buildNodeConfig(u, map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatalf("buildNodeConfig: %v", err)
|
||||
}
|
||||
|
||||
if node.Connector == nil || node.Connector.Auth == nil {
|
||||
t.Fatal("node.Connector.Auth is nil")
|
||||
}
|
||||
if node.Connector.Auth.Username != "aes-256-gcm" {
|
||||
t.Errorf("Username = %q, want %q", node.Connector.Auth.Username, "aes-256-gcm")
|
||||
}
|
||||
if node.Connector.Auth.Password != "password" {
|
||||
t.Errorf("Password = %q, want %q", node.Connector.Auth.Password, "password")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildNodeConfigStandardAuth(t *testing.T) {
|
||||
rawURL := "http://user:pass@server:8080"
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
t.Fatalf("url.Parse: %v", err)
|
||||
}
|
||||
|
||||
node, err := buildNodeConfig(u, map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatalf("buildNodeConfig: %v", err)
|
||||
}
|
||||
|
||||
if node.Connector == nil || node.Connector.Auth == nil {
|
||||
t.Fatal("node.Connector.Auth is nil")
|
||||
}
|
||||
if node.Connector.Auth.Username != "user" {
|
||||
t.Errorf("Username = %q, want %q", node.Connector.Auth.Username, "user")
|
||||
}
|
||||
if node.Connector.Auth.Password != "pass" {
|
||||
t.Errorf("Password = %q, want %q", node.Connector.Auth.Password, "pass")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildServiceConfigSIP002(t *testing.T) {
|
||||
encoded := base64.RawURLEncoding.EncodeToString([]byte("aes-256-gcm:password"))
|
||||
rawURL := "ss://" + encoded + "@:8388"
|
||||
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
t.Fatalf("url.Parse: %v", err)
|
||||
}
|
||||
|
||||
svcs, err := buildServiceConfig(u)
|
||||
if err != nil {
|
||||
t.Fatalf("buildServiceConfig: %v", err)
|
||||
}
|
||||
if len(svcs) == 0 {
|
||||
t.Fatal("no services returned")
|
||||
}
|
||||
|
||||
auth := svcs[0].Handler.Auth
|
||||
if auth == nil {
|
||||
t.Fatal("Handler.Auth is nil")
|
||||
}
|
||||
if auth.Username != "aes-256-gcm" {
|
||||
t.Errorf("Username = %q, want %q", auth.Username, "aes-256-gcm")
|
||||
}
|
||||
if auth.Password != "password" {
|
||||
t.Errorf("Password = %q, want %q", auth.Password, "password")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildServiceConfigStandardAuth(t *testing.T) {
|
||||
rawURL := "http://user:pass@:8080"
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
t.Fatalf("url.Parse: %v", err)
|
||||
}
|
||||
|
||||
svcs, err := buildServiceConfig(u)
|
||||
if err != nil {
|
||||
t.Fatalf("buildServiceConfig: %v", err)
|
||||
}
|
||||
if len(svcs) == 0 {
|
||||
t.Fatal("no services returned")
|
||||
}
|
||||
|
||||
auth := svcs[0].Handler.Auth
|
||||
if auth == nil {
|
||||
t.Fatal("Handler.Auth is nil")
|
||||
}
|
||||
if auth.Username != "user" {
|
||||
t.Errorf("Username = %q, want %q", auth.Username, "user")
|
||||
}
|
||||
if auth.Password != "pass" {
|
||||
t.Errorf("Password = %q, want %q", auth.Password, "pass")
|
||||
}
|
||||
}
|
||||
+80
-52
@@ -121,6 +121,7 @@ type TLSOptions struct {
|
||||
MinVersion string `yaml:"minVersion,omitempty" json:"minVersion,omitempty"`
|
||||
MaxVersion string `yaml:"maxVersion,omitempty" json:"maxVersion,omitempty"`
|
||||
CipherSuites []string `yaml:"cipherSuites,omitempty" json:"cipherSuites,omitempty"`
|
||||
ALPN []string `yaml:"alpn,omitempty" json:"alpn,omitempty"`
|
||||
}
|
||||
|
||||
type PluginConfig struct {
|
||||
@@ -142,8 +143,9 @@ type AutherConfig struct {
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username"`
|
||||
Username string `yaml:",omitempty" json:"username,omitempty"`
|
||||
Password string `yaml:",omitempty" json:"password,omitempty"`
|
||||
File string `yaml:",omitempty" json:"file,omitempty"`
|
||||
}
|
||||
|
||||
type SelectorConfig struct {
|
||||
@@ -154,7 +156,7 @@ type SelectorConfig struct {
|
||||
|
||||
type AdmissionConfig struct {
|
||||
Name string `json:"name"`
|
||||
// DEPRECATED by whitelist since beta.4
|
||||
// Deprecated: use whitelist instead
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
|
||||
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
|
||||
@@ -167,7 +169,7 @@ type AdmissionConfig struct {
|
||||
|
||||
type BypassConfig struct {
|
||||
Name string `json:"name"`
|
||||
// DEPRECATED by whitelist since beta.4
|
||||
// Deprecated: use whitelist instead
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
|
||||
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
|
||||
@@ -185,6 +187,7 @@ type FileLoader struct {
|
||||
type RedisLoader struct {
|
||||
Addr string `json:"addr"`
|
||||
DB int `yaml:",omitempty" json:"db,omitempty"`
|
||||
Username string `yaml:",omitempty" json:"username,omitempty"`
|
||||
Password string `yaml:",omitempty" json:"password,omitempty"`
|
||||
Key string `yaml:",omitempty" json:"key,omitempty"`
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
@@ -250,7 +253,9 @@ type SDConfig struct {
|
||||
}
|
||||
|
||||
type RouterRouteConfig struct {
|
||||
Net string `json:"net"`
|
||||
// Deprecated: use dst instead
|
||||
Net string `yaml:",omitempty" json:"net,omitempty"`
|
||||
Dst string `yaml:",omitempty" json:"dst,omitempty"`
|
||||
Gateway string `json:"gateway"`
|
||||
}
|
||||
|
||||
@@ -274,8 +279,9 @@ type RecorderConfig struct {
|
||||
}
|
||||
|
||||
type FileRecorder struct {
|
||||
Path string `json:"path"`
|
||||
Sep string `yaml:",omitempty" json:"sep,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Sep string `yaml:",omitempty" json:"sep,omitempty"`
|
||||
Rotation *LogRotationConfig `yaml:",omitempty" json:"rotation,omitempty"`
|
||||
}
|
||||
|
||||
type TCPRecorder struct {
|
||||
@@ -292,6 +298,7 @@ type HTTPRecorder struct {
|
||||
type RedisRecorder struct {
|
||||
Addr string `json:"addr"`
|
||||
DB int `yaml:",omitempty" json:"db,omitempty"`
|
||||
Username string `yaml:",omitempty" json:"username,omitempty"`
|
||||
Password string `yaml:",omitempty" json:"password,omitempty"`
|
||||
Key string `yaml:",omitempty" json:"key,omitempty"`
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
@@ -344,7 +351,7 @@ type HandlerConfig struct {
|
||||
}
|
||||
|
||||
type ForwarderConfig struct {
|
||||
// DEPRECATED by hop field
|
||||
// Deprecated: use hop instead
|
||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||
// the referenced hop name
|
||||
Hop string `yaml:",omitempty" json:"hop,omitempty"`
|
||||
@@ -358,18 +365,20 @@ type ForwardNodeConfig struct {
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
// DEPRECATED by filter.protocol
|
||||
// Deprecated: use matcher instead
|
||||
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
||||
// DEPRECATED by filter.host
|
||||
// Deprecated: use matcher instead
|
||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||
// DEPRECATED by filter.path
|
||||
// Deprecated: use matcher instead
|
||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||
// DEPRECATED by http.auth
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
// Deprecated: use matcher instead
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
|
||||
// Deprecated: use http.auth instead
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPURLRewriteConfig struct {
|
||||
@@ -390,13 +399,24 @@ type NodeFilterConfig struct {
|
||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||
}
|
||||
|
||||
type NodeMatcherConfig struct {
|
||||
Rule string `yaml:",omitempty" json:"rule,omitempty"`
|
||||
Priority int `yaml:",omitempty" json:"priority,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPNodeConfig struct {
|
||||
// rewrite host header
|
||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||
// additional request header
|
||||
// Deprecated: use requestHeader instead
|
||||
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
|
||||
// rewrite URL
|
||||
// additional request header
|
||||
RequestHeader map[string]string `yaml:"requestHeader,omitempty" json:"requestHeader,omitempty"`
|
||||
// additional response header
|
||||
ResponseHeader map[string]string `yaml:"responseHeader,omitempty" json:"responseHeader,omitempty"`
|
||||
// Deprecated: use rewriteURL instead
|
||||
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
|
||||
// rewrite URL
|
||||
RewriteURL []HTTPURLRewriteConfig `yaml:"rewriteURL,omitempty" json:"rewriteURL,omitempty"`
|
||||
// rewrite response body
|
||||
RewriteBody []HTTPBodyRewriteConfig `yaml:"rewriteBody,omitempty" json:"rewriteBody,omitempty"`
|
||||
// HTTP basic auth
|
||||
@@ -430,9 +450,9 @@ type SockOptsConfig struct {
|
||||
type ServiceConfig struct {
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
// DEPRECATED by metadata.interface since beta.5
|
||||
// Deprecated: use metadata.interface instead
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
// DEPRECATED by metadata.so_mark since beta.5
|
||||
// Deprecated: use metadata.so_mark instead
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Admission string `yaml:",omitempty" json:"admission,omitempty"`
|
||||
Admissions []string `yaml:",omitempty" json:"admissions,omitempty"`
|
||||
@@ -487,40 +507,47 @@ type ChainGroupConfig struct {
|
||||
}
|
||||
|
||||
type HopConfig struct {
|
||||
Name string `json:"name"`
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
|
||||
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
|
||||
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
|
||||
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
||||
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
Name string `json:"name"`
|
||||
// Deprecated: use metadata.interface instead
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
// Deprecated: use metadata.so_mark instead
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
|
||||
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
|
||||
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
|
||||
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
||||
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
Netns string `yaml:",omitempty" json:"netns,omitempty"`
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||
// Deprecated: use metadata.interface instead
|
||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||
// Deprecated: use metadata.netns instead
|
||||
Netns string `yaml:",omitempty" json:"netns,omitempty"`
|
||||
// Deprecated: use metadata.so_mark instead
|
||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||
// Deprecated: use matcher instead
|
||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
||||
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
|
||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -556,7 +583,8 @@ func (c *Config) Load() error {
|
||||
return v.Unmarshal(c)
|
||||
}
|
||||
|
||||
func (c *Config) Read(r io.Reader) error {
|
||||
func (c *Config) Read(r io.Reader, configType string) error {
|
||||
v.SetConfigType(configType)
|
||||
if err := v.ReadConfig(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package loader
|
||||
|
||||
import (
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
admission_parser "github.com/go-gost/x/config/parsing/admission"
|
||||
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||
chain_parser "github.com/go-gost/x/config/parsing/chain"
|
||||
hop_parser "github.com/go-gost/x/config/parsing/hop"
|
||||
hosts_parser "github.com/go-gost/x/config/parsing/hosts"
|
||||
ingress_parser "github.com/go-gost/x/config/parsing/ingress"
|
||||
limiter_parser "github.com/go-gost/x/config/parsing/limiter"
|
||||
logger_parser "github.com/go-gost/x/config/parsing/logger"
|
||||
observer_parser "github.com/go-gost/x/config/parsing/observer"
|
||||
recorder_parser "github.com/go-gost/x/config/parsing/recorder"
|
||||
resolver_parser "github.com/go-gost/x/config/parsing/resolver"
|
||||
router_parser "github.com/go-gost/x/config/parsing/router"
|
||||
sd_parser "github.com/go-gost/x/config/parsing/sd"
|
||||
service_parser "github.com/go-gost/x/config/parsing/service"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLoader *loader = &loader{}
|
||||
)
|
||||
|
||||
func Load(cfg *config.Config) error {
|
||||
return defaultLoader.Load(cfg)
|
||||
}
|
||||
|
||||
type loader struct{}
|
||||
|
||||
func (l *loader) Load(cfg *config.Config) error {
|
||||
logCfg := cfg.Log
|
||||
if logCfg == nil {
|
||||
logCfg = &config.LogConfig{}
|
||||
}
|
||||
logger.SetDefault(logger_parser.ParseLogger(&config.LoggerConfig{Log: logCfg}))
|
||||
|
||||
tlsCfg, err := parsing.BuildDefaultTLSConfig(cfg.TLS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parsing.SetDefaultTLSConfig(tlsCfg)
|
||||
|
||||
if err := register(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func register(cfg *config.Config) error {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for name := range registry.LoggerRegistry().GetAll() {
|
||||
registry.LoggerRegistry().Unregister(name)
|
||||
}
|
||||
for _, loggerCfg := range cfg.Loggers {
|
||||
if err := registry.LoggerRegistry().Register(loggerCfg.Name, logger_parser.ParseLogger(loggerCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.AutherRegistry().GetAll() {
|
||||
registry.AutherRegistry().Unregister(name)
|
||||
}
|
||||
for _, autherCfg := range cfg.Authers {
|
||||
if err := registry.AutherRegistry().Register(autherCfg.Name, auth_parser.ParseAuther(autherCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.AdmissionRegistry().GetAll() {
|
||||
registry.AdmissionRegistry().Unregister(name)
|
||||
}
|
||||
for _, admissionCfg := range cfg.Admissions {
|
||||
if err := registry.AdmissionRegistry().Register(admissionCfg.Name, admission_parser.ParseAdmission(admissionCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.BypassRegistry().GetAll() {
|
||||
registry.BypassRegistry().Unregister(name)
|
||||
}
|
||||
for _, bypassCfg := range cfg.Bypasses {
|
||||
if err := registry.BypassRegistry().Register(bypassCfg.Name, bypass_parser.ParseBypass(bypassCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.ResolverRegistry().GetAll() {
|
||||
registry.ResolverRegistry().Unregister(name)
|
||||
}
|
||||
for _, resolverCfg := range cfg.Resolvers {
|
||||
r, err := resolver_parser.ParseResolver(resolverCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registry.ResolverRegistry().Register(resolverCfg.Name, r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.HostsRegistry().GetAll() {
|
||||
registry.HostsRegistry().Unregister(name)
|
||||
}
|
||||
for _, hostsCfg := range cfg.Hosts {
|
||||
if err := registry.HostsRegistry().Register(hostsCfg.Name, hosts_parser.ParseHostMapper(hostsCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.IngressRegistry().GetAll() {
|
||||
registry.IngressRegistry().Unregister(name)
|
||||
}
|
||||
for _, ingressCfg := range cfg.Ingresses {
|
||||
if err := registry.IngressRegistry().Register(ingressCfg.Name, ingress_parser.ParseIngress(ingressCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.RouterRegistry().GetAll() {
|
||||
registry.RouterRegistry().Unregister(name)
|
||||
}
|
||||
for _, routerCfg := range cfg.Routers {
|
||||
if err := registry.RouterRegistry().Register(routerCfg.Name, router_parser.ParseRouter(routerCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.SDRegistry().GetAll() {
|
||||
registry.SDRegistry().Unregister(name)
|
||||
}
|
||||
for _, sdCfg := range cfg.SDs {
|
||||
if err := registry.SDRegistry().Register(sdCfg.Name, sd_parser.ParseSD(sdCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.ObserverRegistry().GetAll() {
|
||||
registry.ObserverRegistry().Unregister(name)
|
||||
}
|
||||
for _, observerCfg := range cfg.Observers {
|
||||
if err := registry.ObserverRegistry().Register(observerCfg.Name, observer_parser.ParseObserver(observerCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.RecorderRegistry().GetAll() {
|
||||
registry.RecorderRegistry().Unregister(name)
|
||||
}
|
||||
for _, recorderCfg := range cfg.Recorders {
|
||||
if err := registry.RecorderRegistry().Register(recorderCfg.Name, recorder_parser.ParseRecorder(recorderCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.TrafficLimiterRegistry().GetAll() {
|
||||
registry.TrafficLimiterRegistry().Unregister(name)
|
||||
}
|
||||
for _, limiterCfg := range cfg.Limiters {
|
||||
if err := registry.TrafficLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseTrafficLimiter(limiterCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.ConnLimiterRegistry().GetAll() {
|
||||
registry.ConnLimiterRegistry().Unregister(name)
|
||||
}
|
||||
for _, limiterCfg := range cfg.CLimiters {
|
||||
if err := registry.ConnLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseConnLimiter(limiterCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.RateLimiterRegistry().GetAll() {
|
||||
registry.RateLimiterRegistry().Unregister(name)
|
||||
}
|
||||
for _, limiterCfg := range cfg.RLimiters {
|
||||
if err := registry.RateLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseRateLimiter(limiterCfg)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.HopRegistry().GetAll() {
|
||||
registry.HopRegistry().Unregister(name)
|
||||
}
|
||||
for _, hopCfg := range cfg.Hops {
|
||||
hop, err := hop_parser.ParseHop(hopCfg, logger.Default())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registry.HopRegistry().Register(hopCfg.Name, hop); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.ChainRegistry().GetAll() {
|
||||
registry.ChainRegistry().Unregister(name)
|
||||
}
|
||||
for _, chainCfg := range cfg.Chains {
|
||||
c, err := chain_parser.ParseChain(chainCfg, logger.Default())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := registry.ChainRegistry().Register(chainCfg.Name, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for name := range registry.ServiceRegistry().GetAll() {
|
||||
registry.ServiceRegistry().Unregister(name)
|
||||
}
|
||||
for _, svcCfg := range cfg.Services {
|
||||
svc, err := service_parser.ParseService(svcCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if svc != nil {
|
||||
if err := registry.ServiceRegistry().Register(svcCfg.Name, svc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -59,6 +59,7 @@ func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
|
||||
opts = append(opts, xadmission.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/logger"
|
||||
@@ -67,6 +71,7 @@ func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
||||
opts = append(opts, xauth.RedisLoaderOption(loader.RedisHashLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -97,7 +102,20 @@ func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
|
||||
}
|
||||
|
||||
func Info(cfg *config.AuthConfig) *url.Userinfo {
|
||||
if cfg == nil || cfg.Username == "" {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cfg.File != "" {
|
||||
if f, _ := os.Open(cfg.File); f != nil {
|
||||
defer f.Close()
|
||||
if infos, _ := parseInfo(f, 1); len(infos) > 0 {
|
||||
return infos[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Username == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -107,6 +125,39 @@ func Info(cfg *config.AuthConfig) *url.Userinfo {
|
||||
return url.UserPassword(cfg.Username, cfg.Password)
|
||||
}
|
||||
|
||||
func parseInfo(r io.Reader, max int) (infos []*url.Userinfo, err error) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
// line := strings.Replace(scanner.Text(), "\t", " ", -1)
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if n := strings.IndexByte(line, '#'); n == 0 {
|
||||
continue
|
||||
}
|
||||
sp := strings.SplitN(line, " ", 2)
|
||||
if len(sp) == 1 {
|
||||
if k := strings.TrimSpace(sp[0]); k != "" {
|
||||
infos = append(infos, url.User(k))
|
||||
}
|
||||
}
|
||||
if len(sp) == 2 {
|
||||
if k := strings.TrimSpace(sp[0]); k != "" {
|
||||
infos = append(infos, url.UserPassword(k, strings.TrimSpace(sp[1])))
|
||||
}
|
||||
}
|
||||
|
||||
if max > 0 && len(infos) >= max {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
return
|
||||
}
|
||||
|
||||
func List(name string, names ...string) []auth.Authenticator {
|
||||
var authers []auth.Authenticator
|
||||
if auther := registry.AutherRegistry().Get(name); auther != nil {
|
||||
|
||||
@@ -59,6 +59,7 @@ func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
|
||||
opts = append(opts, xbypass.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
+55
-12
@@ -4,11 +4,10 @@ import (
|
||||
"crypto/tls"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/logger"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/go-gost/x/internal/loader"
|
||||
"github.com/go-gost/x/internal/plugin"
|
||||
"github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
@@ -50,6 +50,8 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var ppv int
|
||||
var soMark int
|
||||
ifce := cfg.Interface
|
||||
var netns string
|
||||
if cfg.Metadata != nil {
|
||||
@@ -57,7 +59,15 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" {
|
||||
ifce = v
|
||||
}
|
||||
netns = mdutil.GetString(md, "netns")
|
||||
|
||||
if cfg.SockOpts != nil {
|
||||
soMark = cfg.SockOpts.Mark
|
||||
}
|
||||
if v := mdutil.GetInt(md, parsing.MDKeySoMark); v > 0 {
|
||||
soMark = v
|
||||
}
|
||||
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
|
||||
netns = mdutil.GetString(md, parsing.MDKeyNetns)
|
||||
}
|
||||
|
||||
var nodes []*chain.Node
|
||||
@@ -66,21 +76,53 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
m := v.Metadata
|
||||
if m == nil {
|
||||
m = map[string]any{}
|
||||
v.Metadata = m
|
||||
}
|
||||
md := metadata.NewMetadata(m)
|
||||
|
||||
if v.Resolver == "" {
|
||||
v.Resolver = cfg.Resolver
|
||||
}
|
||||
if v.Hosts == "" {
|
||||
v.Hosts = cfg.Hosts
|
||||
}
|
||||
if v.Interface == "" {
|
||||
v.Interface = ifce
|
||||
}
|
||||
if v.Netns == "" {
|
||||
v.Netns = netns
|
||||
}
|
||||
|
||||
if v.SockOpts == nil {
|
||||
v.SockOpts = cfg.SockOpts
|
||||
if !md.IsExists(parsing.MDKeyInterface) {
|
||||
// inherit from hop
|
||||
if ifce != "" {
|
||||
m[parsing.MDKeyInterface] = ifce
|
||||
}
|
||||
// node level
|
||||
if v.Interface != "" {
|
||||
m[parsing.MDKeyInterface] = v.Interface
|
||||
}
|
||||
}
|
||||
if !md.IsExists(parsing.MDKeySoMark) {
|
||||
// inherit from hop
|
||||
if soMark != 0 {
|
||||
m[parsing.MDKeySoMark] = soMark
|
||||
}
|
||||
// node level
|
||||
if v.SockOpts != nil && v.SockOpts.Mark != 0 {
|
||||
m[parsing.MDKeySoMark] = v.SockOpts.Mark
|
||||
}
|
||||
}
|
||||
if !md.IsExists(parsing.MDKeyProxyProtocol) && ppv > 0 {
|
||||
// inherit from hop
|
||||
m[parsing.MDKeyProxyProtocol] = ppv
|
||||
}
|
||||
if !md.IsExists(parsing.MDKeyNetns) {
|
||||
// inherit from hop
|
||||
if netns != "" {
|
||||
m[parsing.MDKeyNetns] = netns
|
||||
}
|
||||
// node level
|
||||
if v.Netns != "" {
|
||||
m[parsing.MDKeyNetns] = v.Name
|
||||
}
|
||||
}
|
||||
|
||||
if v.Connector == nil {
|
||||
@@ -115,7 +157,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
xhop.NameOption(cfg.Name),
|
||||
xhop.NodeOption(nodes...),
|
||||
xhop.SelectorOption(sel),
|
||||
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
xhop.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
xhop.ReloadPeriodOption(cfg.Reload),
|
||||
xhop.LoggerOption(log.WithFields(map[string]any{
|
||||
"kind": "hop",
|
||||
@@ -130,6 +172,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
||||
opts = append(opts, xhop.RedisLoaderOption(loader.RedisStringLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -75,6 +75,7 @@ func ParseHostMapper(cfg *config.HostsConfig) hosts.HostMapper {
|
||||
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisListLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -82,6 +83,7 @@ func ParseHostMapper(cfg *config.HostsConfig) hosts.HostMapper {
|
||||
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -70,6 +70,7 @@ func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
|
||||
opts = append(opts, xingress.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -77,6 +78,7 @@ func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
|
||||
opts = append(opts, xingress.RedisLoaderOption(loader.RedisHashLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -57,6 +57,7 @@ func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter)
|
||||
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisListLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -64,6 +65,7 @@ func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter)
|
||||
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -103,6 +105,7 @@ func ParseConnLimiter(cfg *config.LimiterConfig) (lim conn.ConnLimiter) {
|
||||
opts = append(opts, xconn.RedisLoaderOption(loader.RedisListLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -110,6 +113,7 @@ func ParseConnLimiter(cfg *config.LimiterConfig) (lim conn.ConnLimiter) {
|
||||
opts = append(opts, xconn.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -149,6 +153,7 @@ func ParseRateLimiter(cfg *config.LimiterConfig) (lim rate.RateLimiter) {
|
||||
opts = append(opts, xrate.RedisLoaderOption(loader.RedisListLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -156,6 +161,7 @@ func ParseRateLimiter(cfg *config.LimiterConfig) (lim rate.RateLimiter) {
|
||||
opts = append(opts, xrate.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -25,7 +25,7 @@ func ParseLogger(cfg *config.LoggerConfig) logger.Logger {
|
||||
var out io.Writer = os.Stderr
|
||||
switch cfg.Log.Output {
|
||||
case "none", "null":
|
||||
return xlogger.Nop()
|
||||
out = io.Discard
|
||||
case "stdout":
|
||||
out = os.Stdout
|
||||
case "stderr", "":
|
||||
|
||||
@@ -6,14 +6,12 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
xauth "github.com/go-gost/x/auth"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
xchain "github.com/go-gost/x/chain"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
@@ -21,7 +19,9 @@ import (
|
||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
mdx "github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/go-gost/x/routing"
|
||||
)
|
||||
|
||||
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
|
||||
@@ -64,11 +64,6 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nm metadata.Metadata
|
||||
if cfg.Metadata != nil {
|
||||
nm = mdx.NewMetadata(cfg.Metadata)
|
||||
}
|
||||
|
||||
connectorLogger := nodeLogger.WithFields(map[string]any{
|
||||
"kind": "connector",
|
||||
})
|
||||
@@ -104,10 +99,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ppv int
|
||||
if nm != nil {
|
||||
ppv = mdutil.GetInt(nm, parsing.MDKeyProxyProtocol)
|
||||
}
|
||||
md := mdx.NewMetadata(cfg.Metadata)
|
||||
|
||||
dialerLogger := nodeLogger.WithFields(map[string]any{
|
||||
"kind": "dialer",
|
||||
@@ -119,7 +111,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
dialer.AuthOption(auth_parser.Info(cfg.Dialer.Auth)),
|
||||
dialer.TLSConfigOption(tlsConfig),
|
||||
dialer.LoggerOption(dialerLogger),
|
||||
dialer.ProxyProtocolOption(ppv),
|
||||
dialer.ProxyProtocolOption(mdutil.GetInt(md, parsing.MDKeyProxyProtocol)),
|
||||
)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unregistered dialer: %s", cfg.Dialer.Type)
|
||||
@@ -134,25 +126,25 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
}
|
||||
|
||||
var sockOpts *chain.SockOpts
|
||||
if cfg.SockOpts != nil {
|
||||
if v := mdutil.GetInt(md, parsing.MDKeySoMark); v != 0 {
|
||||
sockOpts = &chain.SockOpts{
|
||||
Mark: cfg.SockOpts.Mark,
|
||||
Mark: v,
|
||||
}
|
||||
}
|
||||
|
||||
tr := xchain.NewTransport(d, cr,
|
||||
chain.AddrTransportOption(cfg.Addr),
|
||||
chain.InterfaceTransportOption(cfg.Interface),
|
||||
chain.NetnsTransportOption(cfg.Netns),
|
||||
chain.InterfaceTransportOption(mdutil.GetString(md, parsing.MDKeyInterface)),
|
||||
chain.NetnsTransportOption(mdutil.GetString(md, parsing.MDKeyNetns)),
|
||||
chain.SockOptsTransportOption(sockOpts),
|
||||
)
|
||||
|
||||
opts := []chain.NodeOption{
|
||||
chain.TransportNodeOption(tr),
|
||||
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
chain.BypassNodeOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
||||
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
||||
chain.MetadataNodeOption(nm),
|
||||
chain.MetadataNodeOption(md),
|
||||
chain.NetworkNodeOption(cfg.Network),
|
||||
}
|
||||
|
||||
@@ -175,10 +167,33 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
opts = append(opts, chain.NodeFilterOption(settings))
|
||||
}
|
||||
|
||||
if cfg.Matcher != nil {
|
||||
priority := cfg.Matcher.Priority
|
||||
|
||||
if rule := strings.TrimSpace(cfg.Matcher.Rule); rule != "" {
|
||||
if matcher, err := routing.NewMatcher(rule); err == nil {
|
||||
log.Debugf("new matcher for node %s with rule %s", cfg.Name, cfg.Matcher.Rule)
|
||||
if priority == 0 {
|
||||
priority = len(cfg.Matcher.Rule)
|
||||
}
|
||||
opts = append(opts, chain.MatcherNodeOption(matcher))
|
||||
} else {
|
||||
log.Error(err)
|
||||
priority = -1
|
||||
}
|
||||
}
|
||||
|
||||
opts = append(opts, chain.PriorityNodeOption(priority))
|
||||
}
|
||||
|
||||
if cfg.HTTP != nil {
|
||||
settings := &chain.HTTPNodeSettings{
|
||||
Host: cfg.HTTP.Host,
|
||||
Header: cfg.HTTP.Header,
|
||||
Host: cfg.HTTP.Host,
|
||||
RequestHeader: cfg.HTTP.RequestHeader,
|
||||
ResponseHeader: cfg.HTTP.ResponseHeader,
|
||||
}
|
||||
if settings.RequestHeader == nil {
|
||||
settings.RequestHeader = cfg.HTTP.Header
|
||||
}
|
||||
|
||||
if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
|
||||
@@ -191,7 +206,12 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
})),
|
||||
)
|
||||
}
|
||||
for _, v := range cfg.HTTP.Rewrite {
|
||||
|
||||
rewriteURL := cfg.HTTP.RewriteURL
|
||||
if rewriteURL == nil {
|
||||
rewriteURL = cfg.HTTP.Rewrite
|
||||
}
|
||||
for _, v := range rewriteURL {
|
||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
||||
settings.RewriteURL = append(settings.RewriteURL, chain.HTTPURLRewriteSetting{
|
||||
Pattern: pattern,
|
||||
@@ -201,7 +221,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
}
|
||||
for _, v := range cfg.HTTP.RewriteBody {
|
||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
||||
settings.RewriteBody = append(settings.RewriteBody, chain.HTTPBodyRewriteSettings{
|
||||
settings.RewriteResponseBody = append(settings.RewriteResponseBody, chain.HTTPBodyRewriteSettings{
|
||||
Type: v.Type,
|
||||
Pattern: pattern,
|
||||
Replacement: []byte(v.Replacement),
|
||||
@@ -220,6 +240,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
||||
tlsCfg.Options.MinVersion = o.MinVersion
|
||||
tlsCfg.Options.MaxVersion = o.MaxVersion
|
||||
tlsCfg.Options.CipherSuites = o.CipherSuites
|
||||
tlsCfg.Options.ALPN = o.ALPN
|
||||
}
|
||||
opts = append(opts, chain.TLSNodeOption(tlsCfg))
|
||||
}
|
||||
|
||||
@@ -15,4 +15,18 @@ const (
|
||||
MDKeyRecorderDirection = "direction"
|
||||
MDKeyRecorderTimestampFormat = "timeStampFormat"
|
||||
MDKeyRecorderHexdump = "hexdump"
|
||||
MDKeyRecorderHTTPBody = "http.body"
|
||||
MDKeyRecorderHTTPMaxBodySize = "http.maxBodySize"
|
||||
|
||||
MDKeyLimiterRefreshInterval = "limiter.refreshInterval"
|
||||
MDKeyLimiterCleanupInterval = "limiter.cleanupInterval"
|
||||
MDKeyLimiterScope = "limiter.scope"
|
||||
|
||||
MDKeyObserverResetTraffic = "observer.resetTraffic"
|
||||
MDKeyObserverPeriod = "observer.period"
|
||||
|
||||
MDKeyNetns = "netns"
|
||||
MDKeyNetnsOut = "netns.out"
|
||||
|
||||
MDKeyDialTimeout = "dialTimeout"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/cmd"
|
||||
xmd "github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultParser = &parser{}
|
||||
)
|
||||
|
||||
func Init(args Args) {
|
||||
defaultParser = &parser{
|
||||
args: args,
|
||||
}
|
||||
}
|
||||
|
||||
func Parse() (*config.Config, error) {
|
||||
return defaultParser.Parse()
|
||||
}
|
||||
|
||||
type Args struct {
|
||||
CfgFile string
|
||||
Services []string
|
||||
Nodes []string
|
||||
Debug bool
|
||||
Trace bool
|
||||
ApiAddr string
|
||||
MetricsAddr string
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
args Args
|
||||
}
|
||||
|
||||
func (p *parser) Parse() (*config.Config, error) {
|
||||
cfg := &config.Config{}
|
||||
|
||||
if cfgFile := strings.TrimSpace(p.args.CfgFile); cfgFile != "" {
|
||||
if cfgFile == "-" { // stdin
|
||||
br := bufio.NewReader(os.Stdin)
|
||||
b, err := br.Peek(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b[0] == '{' {
|
||||
if err := cfg.Read(br, "json"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := cfg.Read(br, "yaml"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(cfgFile, "{") && strings.HasSuffix(cfgFile, "}") { // inline
|
||||
if err := json.Unmarshal([]byte(cfgFile), cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := cfg.ReadFile(cfgFile); err != nil { // file
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmdCfg, err := cmd.BuildConfigFromCmd(p.args.Services, p.args.Nodes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg = mergeConfig(cfg, cmdCfg)
|
||||
|
||||
if len(cfg.Services) == 0 && p.args.ApiAddr == "" && cfg.API == nil {
|
||||
if err := cfg.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
||||
cfg.Log = &config.LogConfig{
|
||||
Level: v,
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("GOST_API"); v != "" {
|
||||
cfg.API = &config.APIConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("GOST_METRICS"); v != "" {
|
||||
cfg.Metrics = &config.MetricsConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
||||
cfg.Profiling = &config.ProfilingConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
|
||||
if p.args.Debug || p.args.Trace {
|
||||
if cfg.Log == nil {
|
||||
cfg.Log = &config.LogConfig{}
|
||||
}
|
||||
|
||||
cfg.Log.Level = string(logger.DebugLevel)
|
||||
if p.args.Trace {
|
||||
cfg.Log.Level = string(logger.TraceLevel)
|
||||
}
|
||||
}
|
||||
|
||||
if p.args.ApiAddr != "" {
|
||||
cfg.API = &config.APIConfig{
|
||||
Addr: p.args.ApiAddr,
|
||||
}
|
||||
if url, _ := cmd.Norm(p.args.ApiAddr); url != nil {
|
||||
cfg.API.Addr = url.Host
|
||||
if url.User != nil {
|
||||
username := url.User.Username()
|
||||
password, _ := url.User.Password()
|
||||
cfg.API.Auth = &config.AuthConfig{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
m[k] = v[0]
|
||||
}
|
||||
}
|
||||
md := xmd.NewMetadata(m)
|
||||
cfg.API.PathPrefix = mdutil.GetString(md, "pathPrefix")
|
||||
cfg.API.AccessLog = mdutil.GetBool(md, "accesslog")
|
||||
}
|
||||
}
|
||||
if p.args.MetricsAddr != "" {
|
||||
cfg.Metrics = &config.MetricsConfig{
|
||||
Addr: p.args.MetricsAddr,
|
||||
}
|
||||
if url, _ := cmd.Norm(p.args.MetricsAddr); url != nil {
|
||||
cfg.Metrics.Addr = url.Host
|
||||
if url.User != nil {
|
||||
username := url.User.Username()
|
||||
password, _ := url.User.Password()
|
||||
cfg.Metrics.Auth = &config.AuthConfig{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
}
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
m[k] = v[0]
|
||||
}
|
||||
}
|
||||
md := xmd.NewMetadata(m)
|
||||
cfg.Metrics.Path = mdutil.GetString(md, "path")
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func mergeConfig(cfg1, cfg2 *config.Config) *config.Config {
|
||||
if cfg1 == nil {
|
||||
return cfg2
|
||||
}
|
||||
if cfg2 == nil {
|
||||
return cfg1
|
||||
}
|
||||
|
||||
cfg := &config.Config{
|
||||
Services: append(cfg1.Services, cfg2.Services...),
|
||||
Chains: append(cfg1.Chains, cfg2.Chains...),
|
||||
Hops: append(cfg1.Hops, cfg2.Hops...),
|
||||
Authers: append(cfg1.Authers, cfg2.Authers...),
|
||||
Admissions: append(cfg1.Admissions, cfg2.Admissions...),
|
||||
Bypasses: append(cfg1.Bypasses, cfg2.Bypasses...),
|
||||
Resolvers: append(cfg1.Resolvers, cfg2.Resolvers...),
|
||||
Hosts: append(cfg1.Hosts, cfg2.Hosts...),
|
||||
Ingresses: append(cfg1.Ingresses, cfg2.Ingresses...),
|
||||
SDs: append(cfg1.SDs, cfg2.SDs...),
|
||||
Recorders: append(cfg1.Recorders, cfg2.Recorders...),
|
||||
Limiters: append(cfg1.Limiters, cfg2.Limiters...),
|
||||
CLimiters: append(cfg1.CLimiters, cfg2.CLimiters...),
|
||||
RLimiters: append(cfg1.RLimiters, cfg2.RLimiters...),
|
||||
Loggers: append(cfg1.Loggers, cfg2.Loggers...),
|
||||
Routers: append(cfg1.Routers, cfg2.Routers...),
|
||||
Observers: append(cfg1.Observers, cfg2.Observers...),
|
||||
TLS: cfg1.TLS,
|
||||
Log: cfg1.Log,
|
||||
API: cfg1.API,
|
||||
Metrics: cfg1.Metrics,
|
||||
Profiling: cfg1.Profiling,
|
||||
}
|
||||
if cfg2.TLS != nil {
|
||||
cfg.TLS = cfg2.TLS
|
||||
}
|
||||
if cfg2.Log != nil {
|
||||
cfg.Log = cfg2.Log
|
||||
}
|
||||
if cfg2.API != nil {
|
||||
cfg.API = cfg2.API
|
||||
}
|
||||
if cfg2.Metrics != nil {
|
||||
cfg.Metrics = cfg2.Metrics
|
||||
}
|
||||
if cfg2.Profiling != nil {
|
||||
cfg.Profiling = cfg2.Profiling
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
@@ -2,16 +2,26 @@ package recorder
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/internal/plugin"
|
||||
xrecorder "github.com/go-gost/x/recorder"
|
||||
recorder_plugin "github.com/go-gost/x/recorder/plugin"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
type discardCloser struct{}
|
||||
|
||||
func (discardCloser) Write(p []byte) (n int, err error) { return len(p), nil }
|
||||
func (discardCloser) Close() error { return nil }
|
||||
|
||||
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
@@ -42,13 +52,38 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
||||
}
|
||||
|
||||
if cfg.File != nil && cfg.File.Path != "" {
|
||||
return xrecorder.FileRecorder(cfg.File.Path,
|
||||
xrecorder.SepRecorderOption(cfg.File.Sep),
|
||||
var out io.WriteCloser = discardCloser{}
|
||||
|
||||
if cfg.File.Rotation != nil {
|
||||
out = &lumberjack.Logger{
|
||||
Filename: cfg.File.Path,
|
||||
MaxSize: cfg.File.Rotation.MaxSize,
|
||||
MaxAge: cfg.File.Rotation.MaxAge,
|
||||
MaxBackups: cfg.File.Rotation.MaxBackups,
|
||||
LocalTime: cfg.File.Rotation.LocalTime,
|
||||
Compress: cfg.File.Rotation.Compress,
|
||||
}
|
||||
} else {
|
||||
os.MkdirAll(filepath.Dir(cfg.File.Path), 0755)
|
||||
f, err := os.OpenFile(cfg.File.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
logger.Default().Warn(err)
|
||||
} else {
|
||||
out = f
|
||||
}
|
||||
}
|
||||
|
||||
return xrecorder.FileRecorder(out,
|
||||
xrecorder.RecorderFileRecorderOption(cfg.Name),
|
||||
xrecorder.SepFileRecorderOption(cfg.File.Sep),
|
||||
)
|
||||
}
|
||||
|
||||
if cfg.TCP != nil && cfg.TCP.Addr != "" {
|
||||
return xrecorder.TCPRecorder(cfg.TCP.Addr, xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout))
|
||||
return xrecorder.TCPRecorder(cfg.TCP.Addr,
|
||||
xrecorder.RecorderTCPRecorderOption(cfg.Name),
|
||||
xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout),
|
||||
)
|
||||
}
|
||||
|
||||
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
|
||||
@@ -57,6 +92,7 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
||||
h.Add(k, v)
|
||||
}
|
||||
return xrecorder.HTTPRecorder(cfg.HTTP.URL,
|
||||
xrecorder.RecorderHTTPRecorderOption(cfg.Name),
|
||||
xrecorder.TimeoutHTTPRecorderOption(cfg.HTTP.Timeout),
|
||||
xrecorder.HeaderHTTPRecorderOption(h),
|
||||
)
|
||||
@@ -68,20 +104,26 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
||||
switch cfg.Redis.Type {
|
||||
case "list": // redis list
|
||||
return xrecorder.RedisListRecorder(cfg.Redis.Addr,
|
||||
xrecorder.RecorderRedisRecorderOption(cfg.Name),
|
||||
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
|
||||
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
|
||||
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
|
||||
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
|
||||
)
|
||||
case "sset": // sorted set
|
||||
return xrecorder.RedisSortedSetRecorder(cfg.Redis.Addr,
|
||||
xrecorder.RecorderRedisRecorderOption(cfg.Name),
|
||||
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
|
||||
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
|
||||
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
|
||||
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
|
||||
)
|
||||
default: // redis set
|
||||
return xrecorder.RedisSetRecorder(cfg.Redis.Addr,
|
||||
xrecorder.RecorderRedisRecorderOption(cfg.Name),
|
||||
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
|
||||
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
|
||||
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
|
||||
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -45,18 +45,27 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
|
||||
|
||||
var routes []*router.Route
|
||||
for _, route := range cfg.Routes {
|
||||
_, ipNet, _ := net.ParseCIDR(route.Net)
|
||||
if ipNet == nil {
|
||||
if route == nil {
|
||||
continue
|
||||
}
|
||||
gw := net.ParseIP(route.Gateway)
|
||||
if gw == nil {
|
||||
_, ipNet, _ := net.ParseCIDR(route.Net)
|
||||
dst := route.Dst
|
||||
if dst != "" {
|
||||
_, ipNet, _ = net.ParseCIDR(dst)
|
||||
} else {
|
||||
if ipNet != nil {
|
||||
dst = ipNet.String()
|
||||
}
|
||||
}
|
||||
|
||||
if dst == "" || route.Gateway == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
routes = append(routes, &router.Route{
|
||||
Net: ipNet,
|
||||
Gateway: gw,
|
||||
Dst: dst,
|
||||
Gateway: route.Gateway,
|
||||
})
|
||||
}
|
||||
opts := []xrouter.Option{
|
||||
@@ -76,6 +85,7 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
|
||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisListLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -83,6 +93,7 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
|
||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisSetLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
@@ -90,6 +101,7 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
|
||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisHashLoader(
|
||||
cfg.Redis.Addr,
|
||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
||||
loader.UsernameRedisLoaderOption(cfg.Redis.Username),
|
||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
||||
)))
|
||||
|
||||
@@ -47,6 +47,8 @@ func ParseNodeSelector(cfg *config.SelectorConfig) selector.Selector[*chain.Node
|
||||
strategy = xs.FIFOStrategy[*chain.Node]()
|
||||
case "hash":
|
||||
strategy = xs.HashStrategy[*chain.Node]()
|
||||
case "parallel":
|
||||
strategy = xs.ParallelStrategy[*chain.Node]()
|
||||
default:
|
||||
strategy = xs.RoundRobinStrategy[*chain.Node]()
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/admission"
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/bypass"
|
||||
"github.com/go-gost/core/chain"
|
||||
"github.com/go-gost/core/handler"
|
||||
"github.com/go-gost/core/hop"
|
||||
"github.com/go-gost/core/listener"
|
||||
"github.com/go-gost/core/logger"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
"github.com/go-gost/core/observer/stats"
|
||||
"github.com/go-gost/core/recorder"
|
||||
"github.com/go-gost/core/selector"
|
||||
"github.com/go-gost/core/service"
|
||||
xadmission "github.com/go-gost/x/admission"
|
||||
xauth "github.com/go-gost/x/auth"
|
||||
xbypass "github.com/go-gost/x/bypass"
|
||||
xchain "github.com/go-gost/x/chain"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
@@ -29,7 +29,10 @@ import (
|
||||
logger_parser "github.com/go-gost/x/config/parsing/logger"
|
||||
selector_parser "github.com/go-gost/x/config/parsing/selector"
|
||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||
cache_limiter "github.com/go-gost/x/limiter/traffic/cache"
|
||||
"github.com/go-gost/x/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
xstats "github.com/go-gost/x/observer/stats"
|
||||
"github.com/go-gost/x/registry"
|
||||
xservice "github.com/go-gost/x/service"
|
||||
"github.com/vishvananda/netns"
|
||||
@@ -73,6 +76,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = parsing.DefaultTLSConfig().Clone()
|
||||
tls_util.SetTLSOptions(tlsConfig, tlsCfg.Options)
|
||||
}
|
||||
|
||||
authers := auth_parser.List(cfg.Listener.Auther, cfg.Listener.Authers...)
|
||||
@@ -83,7 +87,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
var auther auth.Authenticator
|
||||
if len(authers) > 0 {
|
||||
auther = auth.AuthenticatorGroup(authers...)
|
||||
auther = xauth.AuthenticatorGroup(authers...)
|
||||
}
|
||||
|
||||
admissions := admission_parser.List(cfg.Admission, cfg.Admissions...)
|
||||
@@ -99,10 +103,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
ifce := cfg.Interface
|
||||
var preUp, preDown, postUp, postDown []string
|
||||
var ignoreChain bool
|
||||
var pStats *stats.Stats
|
||||
var observePeriod time.Duration
|
||||
var pStats stats.Stats
|
||||
var observerPeriod time.Duration
|
||||
var netnsIn, netnsOut string
|
||||
var dialTimeout time.Duration
|
||||
|
||||
var limiterRefreshInterval time.Duration
|
||||
var limiterCleanupInterval time.Duration
|
||||
var limiterScope string
|
||||
|
||||
if cfg.Metadata != nil {
|
||||
md := metadata.NewMetadata(cfg.Metadata)
|
||||
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
|
||||
@@ -121,12 +130,18 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
|
||||
|
||||
if mdutil.GetBool(md, parsing.MDKeyEnableStats) {
|
||||
pStats = &stats.Stats{}
|
||||
pStats = xstats.NewStats(mdutil.GetBool(md, parsing.MDKeyObserverResetTraffic))
|
||||
}
|
||||
observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||
netnsIn = mdutil.GetString(md, "netns")
|
||||
netnsOut = mdutil.GetString(md, "netns.out")
|
||||
dialTimeout = mdutil.GetDuration(md, "dialTimeout")
|
||||
observerPeriod = mdutil.GetDuration(md, parsing.MDKeyObserverPeriod, "observePeriod")
|
||||
|
||||
netnsIn = mdutil.GetString(md, parsing.MDKeyNetns)
|
||||
netnsOut = mdutil.GetString(md, parsing.MDKeyNetnsOut)
|
||||
|
||||
dialTimeout = mdutil.GetDuration(md, parsing.MDKeyDialTimeout)
|
||||
|
||||
limiterRefreshInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterRefreshInterval)
|
||||
limiterCleanupInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterCleanupInterval)
|
||||
limiterScope = mdutil.GetString(md, parsing.MDKeyLimiterScope)
|
||||
}
|
||||
|
||||
listenerLogger := serviceLogger.WithFields(map[string]any{
|
||||
@@ -154,8 +169,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
listener.AutherOption(auther),
|
||||
listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
|
||||
listener.TLSConfigOption(tlsConfig),
|
||||
listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
||||
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
|
||||
listener.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
|
||||
listener.TrafficLimiterOption(
|
||||
cache_limiter.NewCachedTrafficLimiter(
|
||||
registry.TrafficLimiterRegistry().Get(cfg.Limiter),
|
||||
cache_limiter.RefreshIntervalOption(limiterRefreshInterval),
|
||||
cache_limiter.CleanupIntervalOption(limiterCleanupInterval),
|
||||
cache_limiter.ScopeOption(limiterScope),
|
||||
),
|
||||
),
|
||||
listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
|
||||
listener.ServiceOption(cfg.Name),
|
||||
listener.ProxyProtocolOption(ppv),
|
||||
@@ -222,6 +244,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = parsing.DefaultTLSConfig().Clone()
|
||||
tls_util.SetTLSOptions(tlsConfig, tlsCfg.Options)
|
||||
}
|
||||
|
||||
authers = auth_parser.List(cfg.Handler.Auther, cfg.Handler.Authers...)
|
||||
@@ -233,7 +256,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
|
||||
auther = nil
|
||||
if len(authers) > 0 {
|
||||
auther = auth.AuthenticatorGroup(authers...)
|
||||
auther = xauth.AuthenticatorGroup(authers...)
|
||||
}
|
||||
|
||||
var recorders []recorder.RecorderObject
|
||||
@@ -246,6 +269,8 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
Direction: mdutil.GetBool(md, parsing.MDKeyRecorderDirection),
|
||||
TimestampFormat: mdutil.GetString(md, parsing.MDKeyRecorderTimestampFormat),
|
||||
Hexdump: mdutil.GetBool(md, parsing.MDKeyRecorderHexdump),
|
||||
HTTPBody: mdutil.GetBool(md, parsing.MDKeyRecorderHTTPBody),
|
||||
MaxBodySize: mdutil.GetInt(md, parsing.MDKeyRecorderHTTPMaxBodySize),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -273,7 +298,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
handler.RouterOption(xchain.NewRouter(routerOpts...)),
|
||||
handler.AutherOption(auther),
|
||||
handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
|
||||
handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
handler.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||
handler.TLSConfigOption(tlsConfig),
|
||||
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
|
||||
handler.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Handler.Limiter)),
|
||||
@@ -305,7 +330,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
}
|
||||
|
||||
s := xservice.NewService(cfg.Name, ln, h,
|
||||
xservice.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
||||
xservice.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
|
||||
xservice.PreUpOption(preUp),
|
||||
xservice.PreDownOption(preDown),
|
||||
xservice.PostUpOption(postUp),
|
||||
@@ -313,7 +338,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
xservice.RecordersOption(recorders...),
|
||||
xservice.StatsOption(pStats),
|
||||
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
|
||||
xservice.ObservePeriodOption(observePeriod),
|
||||
xservice.ObserverPeriodOption(observerPeriod),
|
||||
xservice.LoggerOption(serviceLogger),
|
||||
)
|
||||
|
||||
@@ -370,6 +395,7 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er
|
||||
Bypass: node.Bypass,
|
||||
Bypasses: node.Bypasses,
|
||||
Filter: filter,
|
||||
Matcher: node.Matcher,
|
||||
HTTP: httpCfg,
|
||||
TLS: node.TLS,
|
||||
Metadata: node.Metadata,
|
||||
|
||||
+12
-5
@@ -12,6 +12,7 @@ import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
@@ -20,14 +21,19 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTLSConfig *tls.Config
|
||||
defaultTLSConfig atomic.Value
|
||||
)
|
||||
|
||||
func DefaultTLSConfig() *tls.Config {
|
||||
return defaultTLSConfig
|
||||
v, _ := defaultTLSConfig.Load().(*tls.Config)
|
||||
return v
|
||||
}
|
||||
|
||||
func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
|
||||
func SetDefaultTLSConfig(cfg *tls.Config) {
|
||||
defaultTLSConfig.Store(cfg)
|
||||
}
|
||||
|
||||
func BuildDefaultTLSConfig(cfg *config.TLSConfig) (*tls.Config, error) {
|
||||
log := logger.Default()
|
||||
|
||||
if cfg == nil {
|
||||
@@ -43,7 +49,7 @@ func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
|
||||
// generate random self-signed certificate.
|
||||
cert, err := genCertificate(cfg.Validity, cfg.Organization, cfg.CommonName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
@@ -52,7 +58,8 @@ func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
|
||||
} else {
|
||||
log.Debug("load global TLS certificate files OK")
|
||||
}
|
||||
defaultTLSConfig = tlsConfig
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func genCertificate(validity time.Duration, org string, cn string) (cert tls.Certificate, err error) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) Read(b []byte) (n int, err error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (c *conn) Write(b []byte) (n int, err error) {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
func (c *conn) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (c *conn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{}
|
||||
}
|
||||
|
||||
func (c *conn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package forward
|
||||
package direct
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -15,6 +16,7 @@ func init() {
|
||||
}
|
||||
|
||||
type directConnector struct {
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
|
||||
@@ -30,7 +32,7 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
func (c *directConnector) Init(md md.Metadata) (err error) {
|
||||
return nil
|
||||
return c.parseMetadata(md)
|
||||
}
|
||||
|
||||
func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
@@ -39,6 +41,10 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
|
||||
opt(&cOpts)
|
||||
}
|
||||
|
||||
if c.md.action == "reject" {
|
||||
return &conn{}, nil
|
||||
}
|
||||
|
||||
conn, err := cOpts.Dialer.Dial(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -57,6 +63,7 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
|
||||
"local": localAddr,
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package direct
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
action string
|
||||
}
|
||||
|
||||
func (c *directConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
c.md.action = strings.ToLower(mdutil.GetString(md, "action"))
|
||||
return
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ func (c *forwardConnector) Connect(ctx context.Context, conn net.Conn, network,
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/util/socks"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
@@ -48,6 +49,7 @@ func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
ictx "github.com/go-gost/x/internal/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -48,11 +50,17 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(xctx.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
v, _ := conn.(md.Metadatable)
|
||||
if v == nil {
|
||||
var client *http.Client
|
||||
if cc, ok := conn.(xctx.Context); ok {
|
||||
if md := ictx.MetadataFromContext(cc.Context()); md != nil {
|
||||
client, _ = md.Get("client").(*http.Client)
|
||||
}
|
||||
}
|
||||
if client == nil {
|
||||
err := errors.New("http2: wrong connection type")
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
@@ -67,7 +75,6 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
ProtoMinor: 0,
|
||||
Header: c.md.header,
|
||||
Body: pr,
|
||||
// ContentLength: -1,
|
||||
}
|
||||
if req.Header == nil {
|
||||
req.Header = make(http.Header)
|
||||
@@ -90,8 +97,7 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
client := v.Metadata().Get("client").(*http.Client)
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Do(req.WithContext(context.WithoutCancel(ctx)))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
conn.Close()
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package masque
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
masque_dialer "github.com/go-gost/x/dialer/http3/masque"
|
||||
masque_util "github.com/go-gost/x/internal/util/masque"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ConnectorRegistry().Register("masque", NewConnector)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidConnection = errors.New("masque: invalid connection type, expected MasqueConn")
|
||||
ErrConnectFailed = errors.New("masque: CONNECT-UDP failed")
|
||||
ErrCapsuleRequired = errors.New("masque: server did not confirm capsule-protocol")
|
||||
)
|
||||
|
||||
type masqueConnector struct {
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
|
||||
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
options := connector.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
return &masqueConnector{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *masqueConnector) Init(md md.Metadata) (err error) {
|
||||
return c.parseMetadata(md)
|
||||
}
|
||||
|
||||
func (c *masqueConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
log := c.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
|
||||
// Only support UDP
|
||||
if !strings.HasPrefix(network, "udp") {
|
||||
return nil, fmt.Errorf("masque connector only supports UDP, got %s", network)
|
||||
}
|
||||
|
||||
log.Debugf("connect-udp %s/%s", address, network)
|
||||
|
||||
// Get the MasqueConn from the dialer
|
||||
masqueConn, ok := conn.(*masque_dialer.MasqueConn)
|
||||
if !ok {
|
||||
log.Error(ErrInvalidConnection)
|
||||
return nil, ErrInvalidConnection
|
||||
}
|
||||
|
||||
// Get pre-opened stream from dialer (stream opening happens there for dead connection detection)
|
||||
reqStream := masqueConn.GetRequestStream()
|
||||
proxyHost := masqueConn.GetHost()
|
||||
|
||||
// Apply connect timeout to the actual stream
|
||||
if c.md.connectTimeout > 0 {
|
||||
reqStream.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||
defer reqStream.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
// Ensure stream is closed on any error
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
reqStream.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Build CONNECT-UDP path
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("masque: invalid address %s: %w", address, err)
|
||||
}
|
||||
|
||||
path := masque_util.BuildMasquePath(host, mustAtoi(port))
|
||||
|
||||
// Create CONNECT-UDP request
|
||||
// For Extended CONNECT (RFC 9220), set Proto to the :protocol value
|
||||
// quic-go translates req.Proto to the :protocol pseudo-header
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Path: path},
|
||||
Host: proxyHost,
|
||||
Header: http.Header{
|
||||
"Capsule-Protocol": []string{"?1"},
|
||||
},
|
||||
Proto: "connect-udp", // This becomes the :protocol pseudo-header
|
||||
ProtoMajor: 3,
|
||||
}
|
||||
|
||||
// Add proxy authentication if configured
|
||||
// Use Proxy-Authorization header (not Authorization) for proxy auth
|
||||
if c.options.Auth != nil {
|
||||
u := c.options.Auth.Username()
|
||||
p, _ := c.options.Auth.Password()
|
||||
auth := u + ":" + p
|
||||
encoded := base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
req.Header.Set("Proxy-Authorization", "Basic "+encoded)
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("CONNECT-UDP request: %s %s Host=%s", req.Method, path, proxyHost)
|
||||
}
|
||||
log.Debugf("sending CONNECT-UDP request to %s", path)
|
||||
|
||||
// Send request headers
|
||||
if err := reqStream.SendRequestHeader(req); err != nil {
|
||||
log.Error("masque: failed to send request:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read response
|
||||
resp, err := reqStream.ReadResponse()
|
||||
if err != nil {
|
||||
log.Error("masque: failed to read response:", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if log.IsLevelEnabled(logger.TraceLevel) {
|
||||
log.Tracef("CONNECT-UDP response: %d %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Errorf("masque: proxy returned status %d", resp.StatusCode)
|
||||
return nil, fmt.Errorf("%w: status %d", ErrConnectFailed, resp.StatusCode)
|
||||
}
|
||||
|
||||
if resp.Header.Get("Capsule-Protocol") != "?1" {
|
||||
log.Error(ErrCapsuleRequired)
|
||||
return nil, ErrCapsuleRequired
|
||||
}
|
||||
|
||||
log.Debugf("CONNECT-UDP established to %s", address)
|
||||
|
||||
// Get the underlying HTTP/3 stream for datagrams
|
||||
stream := reqStream
|
||||
|
||||
// Resolve target address
|
||||
raddr, err := net.ResolveUDPAddr("udp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create datagram connection wrapping the request stream
|
||||
datagramConn := masque_util.NewDatagramConnFromRequestStream(stream, conn.LocalAddr(), raddr)
|
||||
|
||||
success = true // Prevent defer from closing stream - datagramConn now owns it
|
||||
return datagramConn, nil
|
||||
}
|
||||
|
||||
func mustAtoi(s string) int {
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package masque
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
connectTimeout time.Duration
|
||||
}
|
||||
|
||||
func (c *masqueConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
c.md.connectTimeout = mdutil.GetDuration(md, "timeout", "connectTimeout")
|
||||
return nil
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (c *relayConnector) bindTCP(ctx context.Context, conn net.Conn, network, ad
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("bind on %s/%s OK", laddr, laddr.Network())
|
||||
log.Infof("bind on %s/%s OK", laddr, laddr.Network())
|
||||
|
||||
session, err := mux.ServerSession(conn, c.md.muxCfg)
|
||||
if err != nil {
|
||||
@@ -63,7 +63,7 @@ func (c *relayConnector) bindUDP(ctx context.Context, conn net.Conn, network, ad
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("bind on %s/%s OK", laddr, laddr.Network())
|
||||
log.Infof("bind on %s/%s OK", laddr, laddr.Network())
|
||||
|
||||
ln := udp.NewListener(
|
||||
relay_util.UDPTunClientPacketConn(conn),
|
||||
|
||||
@@ -192,14 +192,13 @@ func (c *bindUDPConn) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 2-byte data length header
|
||||
var bh [2]byte
|
||||
binary.BigEndian.PutUint16(bh[:], uint16(len(b)))
|
||||
_, err = c.Conn.Write(bh[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
buf := bufpool.Get(len(b) + 2)
|
||||
defer bufpool.Put(buf)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
|
||||
n = copy(buf[2:], b)
|
||||
|
||||
return c.Conn.Write(buf)
|
||||
}
|
||||
|
||||
func (c *bindUDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/relay"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
relay_util "github.com/go-gost/x/internal/util/relay"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
@@ -44,6 +45,7 @@ func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
|
||||
"github.com/go-gost/core/common/bufpool"
|
||||
"github.com/go-gost/relay"
|
||||
xrelay "github.com/go-gost/x/internal/util/relay"
|
||||
)
|
||||
|
||||
type packetConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (c *packetConn) Read(b []byte) (n int, err error) {
|
||||
var bb [2]byte
|
||||
_, err = io.ReadFull(c.Conn, bb[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dlen := int(binary.BigEndian.Uint16(bb[:]))
|
||||
if len(b) >= dlen {
|
||||
return io.ReadFull(c.Conn, b[:dlen])
|
||||
}
|
||||
|
||||
buf := bufpool.Get(dlen)
|
||||
defer bufpool.Put(buf)
|
||||
|
||||
n, err = io.ReadFull(c.Conn, buf)
|
||||
copy(b, buf[:n])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *packetConn) Write(b []byte) (n int, err error) {
|
||||
if len(b) > math.MaxUint16 {
|
||||
err = errors.New("write: data maximum exceeded")
|
||||
return
|
||||
}
|
||||
|
||||
buf := bufpool.Get(len(b) + 2)
|
||||
defer bufpool.Put(buf)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
|
||||
n = copy(buf[2:], b)
|
||||
|
||||
return c.Conn.Write(buf)
|
||||
}
|
||||
|
||||
func readResponse(r io.Reader) (err error) {
|
||||
resp := relay.Response{}
|
||||
_, err = resp.ReadFrom(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.Version != relay.Version1 {
|
||||
err = relay.ErrBadVersion
|
||||
return
|
||||
}
|
||||
|
||||
if resp.Status != relay.StatusOK {
|
||||
err = fmt.Errorf("%d %s", resp.Status, xrelay.StatusText(resp.Status))
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/relay"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.ConnectorRegistry().Register("router", NewConnector)
|
||||
}
|
||||
|
||||
type routerConnector struct {
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
|
||||
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
options := connector.Options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
return &routerConnector{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *routerConnector) Init(md md.Metadata) (err error) {
|
||||
return c.parseMetadata(md)
|
||||
}
|
||||
|
||||
func (c *routerConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
log := c.options.Logger.WithFields(map[string]any{
|
||||
"network": network,
|
||||
"address": address,
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": xctx.SidFromContext(ctx).String(),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
if c.md.connectTimeout > 0 {
|
||||
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
req := relay.Request{
|
||||
Version: relay.Version1,
|
||||
Cmd: relay.CmdAssociate,
|
||||
}
|
||||
|
||||
if c.options.Auth != nil {
|
||||
pwd, _ := c.options.Auth.Password()
|
||||
req.Features = append(req.Features, &relay.UserAuthFeature{
|
||||
Username: c.options.Auth.Username(),
|
||||
Password: pwd,
|
||||
})
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "udp":
|
||||
req.Features = append(req.Features, &relay.NetworkFeature{
|
||||
Network: relay.NetworkUDP,
|
||||
})
|
||||
|
||||
case "ip":
|
||||
req.Features = append(req.Features, &relay.NetworkFeature{
|
||||
Network: relay.NetworkIP,
|
||||
})
|
||||
}
|
||||
|
||||
srcAddr := conn.RemoteAddr().String()
|
||||
if v := xctx.SrcAddrFromContext(ctx); v != nil {
|
||||
srcAddr = v.String()
|
||||
}
|
||||
|
||||
af := &relay.AddrFeature{}
|
||||
af.ParseFrom(srcAddr)
|
||||
req.Features = append(req.Features, af) // src address
|
||||
|
||||
af = &relay.AddrFeature{}
|
||||
af.ParseFrom(address)
|
||||
req.Features = append(req.Features, af) // dst address
|
||||
|
||||
if !c.md.routerID.IsZero() {
|
||||
req.Features = append(req.Features, &relay.TunnelFeature{
|
||||
ID: c.md.routerID,
|
||||
})
|
||||
}
|
||||
|
||||
if _, err := req.WriteTo(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// drain the response
|
||||
if err := readResponse(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "udp", "ip":
|
||||
conn = &packetConn{
|
||||
Conn: conn,
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("network %s is unsupported", network)
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("connect to router %s@%s OK", address, c.md.routerID)
|
||||
return conn, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/relay"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidRouterID = errors.New("router: invalid router ID")
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
connectTimeout time.Duration
|
||||
routerID relay.TunnelID
|
||||
}
|
||||
|
||||
func (c *routerConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
c.md.connectTimeout = mdutil.GetDuration(md, "connectTimeout")
|
||||
|
||||
if s := mdutil.GetString(md, "router.id"); s != "" {
|
||||
uuid, err := uuid.Parse(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.md.routerID = relay.NewTunnelID(uuid[:])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ func (c *serialConnector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -39,6 +40,7 @@ func (c *sniConnector) Connect(ctx context.Context, conn net.Conn, network, addr
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/gosocks4"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -45,6 +46,7 @@ func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/gosocks5"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/util/socks"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
@@ -97,6 +98,7 @@ func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+20
-12
@@ -9,10 +9,12 @@ import (
|
||||
"github.com/go-gost/core/common/bufpool"
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/go-shadowsocks2/core"
|
||||
"github.com/go-gost/go-shadowsocks2/socks"
|
||||
"github.com/go-gost/go-shadowsocks2/utils"
|
||||
"github.com/go-gost/gosocks5"
|
||||
"github.com/go-gost/x/internal/util/ss"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -20,7 +22,7 @@ func init() {
|
||||
}
|
||||
|
||||
type ssConnector struct {
|
||||
cipher core.Cipher
|
||||
client core.TCPClient
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
@@ -44,7 +46,13 @@ func (c *ssConnector) Init(md md.Metadata) (err error) {
|
||||
if c.options.Auth != nil {
|
||||
method := c.options.Auth.Username()
|
||||
password, _ := c.options.Auth.Password()
|
||||
c.cipher, err = ss.ShadowCipher(method, password, c.md.key)
|
||||
|
||||
clientConfig, err := utils.NewClientConfig(method, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.client = core.NewTCPClient(clientConfig)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -56,6 +64,7 @@ func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, addre
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
@@ -91,21 +100,20 @@ func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, addre
|
||||
defer conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
if c.cipher != nil {
|
||||
conn = c.cipher.StreamConn(conn)
|
||||
target := socks.Addr(rawaddr[:n])
|
||||
conn, err = c.client.WrapConn(conn, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sc net.Conn
|
||||
if c.md.noDelay {
|
||||
sc = ss.ShadowConn(conn, nil)
|
||||
// write the addr at once.
|
||||
if _, err := sc.Write(rawaddr[:n]); err != nil {
|
||||
err := conn.(core.TCPConn).ClientFirstWrite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// cache the header
|
||||
sc = ss.ShadowConn(conn, rawaddr[:n])
|
||||
}
|
||||
sc = conn
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -4,14 +4,18 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/go-shadowsocks2/core"
|
||||
"github.com/go-gost/go-shadowsocks2/socks"
|
||||
"github.com/go-gost/go-shadowsocks2/utils"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/util/relay"
|
||||
"github.com/go-gost/x/internal/util/ss"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -19,9 +23,11 @@ func init() {
|
||||
}
|
||||
|
||||
type ssuConnector struct {
|
||||
cipher core.Cipher
|
||||
md metadata
|
||||
options connector.Options
|
||||
client core.UDPClient
|
||||
tcpClient core.TCPClient
|
||||
md metadata
|
||||
options connector.Options
|
||||
sessionMap sync.Map
|
||||
}
|
||||
|
||||
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
@@ -43,7 +49,12 @@ func (c *ssuConnector) Init(md md.Metadata) (err error) {
|
||||
if c.options.Auth != nil {
|
||||
method := c.options.Auth.Username()
|
||||
password, _ := c.options.Auth.Password()
|
||||
c.cipher, err = ss.ShadowCipher(method, password, c.md.key)
|
||||
clientConfig, err := utils.NewClientConfig(method, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.client = core.NewUDPClient(clientConfig, 60)
|
||||
c.tcpClient = core.NewTCPClient(clientConfig)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -55,6 +66,7 @@ func (c *ssuConnector) Connect(ctx context.Context, conn net.Conn, network, addr
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
@@ -78,16 +90,14 @@ func (c *ssuConnector) Connect(ctx context.Context, conn net.Conn, network, addr
|
||||
|
||||
pc, ok := conn.(net.PacketConn)
|
||||
if ok {
|
||||
if c.cipher != nil {
|
||||
pc = c.cipher.PacketConn(pc)
|
||||
}
|
||||
|
||||
// standard UDP relay
|
||||
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.bufferSize), nil
|
||||
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.udpBufferSize, &c.client, &c.sessionMap), nil
|
||||
}
|
||||
|
||||
if c.cipher != nil {
|
||||
conn = ss.ShadowConn(c.cipher.StreamConn(conn), nil)
|
||||
target := socks.ParseAddr(taddr.String())
|
||||
conn, err := c.tcpClient.WrapConn(conn, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// UDP over TCP
|
||||
|
||||
@@ -1,34 +1,22 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
key string
|
||||
connectTimeout time.Duration
|
||||
bufferSize int
|
||||
udpBufferSize int
|
||||
}
|
||||
|
||||
func (c *ssuConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
const (
|
||||
key = "key"
|
||||
connectTimeout = "timeout"
|
||||
bufferSize = "bufferSize" // udp buffer size
|
||||
)
|
||||
|
||||
c.md.key = mdutil.GetString(md, key)
|
||||
c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout)
|
||||
|
||||
if bs := mdutil.GetInt(md, bufferSize); bs > 0 {
|
||||
c.md.bufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
|
||||
} else {
|
||||
c.md.bufferSize = 4096
|
||||
}
|
||||
c.md.key = mdutil.GetString(md, "key")
|
||||
c.md.connectTimeout = mdutil.GetDuration(md, "timeout", "connectTimeout")
|
||||
c.md.udpBufferSize = mdutil.GetInt(md, "udpBufferSize", "udp.bufferSize")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
ssh_util "github.com/go-gost/x/internal/util/ssh"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
@@ -40,6 +41,7 @@ func (c *sshdConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ func (c *tcpConnector) Connect(ctx context.Context, conn net.Conn, network, addr
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
@@ -130,14 +130,13 @@ func (c *bindUDPConn) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 2-byte data length header
|
||||
var bh [2]byte
|
||||
binary.BigEndian.PutUint16(bh[:], uint16(len(b)))
|
||||
_, err = c.Conn.Write(bh[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
buf := bufpool.Get(len(b) + 2)
|
||||
defer bufpool.Put(buf)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
|
||||
n = copy(buf[2:], b)
|
||||
|
||||
return c.Conn.Write(buf)
|
||||
}
|
||||
|
||||
func (c *bindUDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
"github.com/go-gost/relay"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -39,10 +39,11 @@ func (c *tunnelConnector) Init(md md.Metadata) (err error) {
|
||||
|
||||
func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||
log := c.options.Logger.WithFields(map[string]any{
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"remote": conn.RemoteAddr().String(),
|
||||
"local": conn.LocalAddr().String(),
|
||||
"sid": xctx.SidFromContext(ctx).String(),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
@@ -72,9 +73,9 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
})
|
||||
}
|
||||
|
||||
srcAddr := conn.LocalAddr().String()
|
||||
if v := ctxvalue.ClientAddrFromContext(ctx); v != "" {
|
||||
srcAddr = string(v)
|
||||
srcAddr := conn.RemoteAddr().String()
|
||||
if v := xctx.SrcAddrFromContext(ctx); v != nil {
|
||||
srcAddr = v.String()
|
||||
}
|
||||
|
||||
af := &relay.AddrFeature{}
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
"github.com/go-gost/relay"
|
||||
"github.com/go-gost/x/internal/util/mux"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -56,6 +56,9 @@ func (c *tunnelConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||
if c.md.muxCfg.Version == 0 {
|
||||
c.md.muxCfg.Version = 2
|
||||
}
|
||||
if c.md.muxCfg.MaxStreamBuffer == 0 {
|
||||
c.md.muxCfg.MaxStreamBuffer = 1048576
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-gost/core/connector"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
ctxvalue "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ func (c *unixConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
"local": conn.LocalAddr().String(),
|
||||
"network": network,
|
||||
"address": address,
|
||||
"sid": string(ctxvalue.SidFromContext(ctx)),
|
||||
})
|
||||
log.Debugf("connect %s/%s", address, network)
|
||||
|
||||
|
||||
+54
-38
@@ -1,76 +1,92 @@
|
||||
package ctx
|
||||
|
||||
import "context"
|
||||
|
||||
// clientAddrKey saves the client address.
|
||||
type clientAddrKey struct{}
|
||||
|
||||
type ClientAddr string
|
||||
|
||||
var (
|
||||
keyClientAddr clientAddrKey
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
func ContextWithClientAddr(ctx context.Context, addr ClientAddr) context.Context {
|
||||
return context.WithValue(ctx, keyClientAddr, addr)
|
||||
type Context interface {
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
func ClientAddrFromContext(ctx context.Context) ClientAddr {
|
||||
v, _ := ctx.Value(keyClientAddr).(ClientAddr)
|
||||
type WithContext interface {
|
||||
WithContext(ctx context.Context)
|
||||
}
|
||||
|
||||
type srcAddrKey struct{}
|
||||
|
||||
func ContextWithSrcAddr(ctx context.Context, addr net.Addr) context.Context {
|
||||
return context.WithValue(ctx, srcAddrKey{}, addr)
|
||||
}
|
||||
|
||||
func SrcAddrFromContext(ctx context.Context) net.Addr {
|
||||
v, _ := ctx.Value(srcAddrKey{}).(net.Addr)
|
||||
return v
|
||||
}
|
||||
|
||||
// sidKey saves the session ID.
|
||||
type sidKey struct{}
|
||||
type Sid string
|
||||
type dstAddrKey struct{}
|
||||
|
||||
var (
|
||||
keySid sidKey
|
||||
func ContextWithDstAddr(ctx context.Context, addr net.Addr) context.Context {
|
||||
return context.WithValue(ctx, dstAddrKey{}, addr)
|
||||
}
|
||||
|
||||
func DstAddrFromContext(ctx context.Context) net.Addr {
|
||||
v, _ := ctx.Value(dstAddrKey{}).(net.Addr)
|
||||
return v
|
||||
}
|
||||
|
||||
type (
|
||||
Sid string
|
||||
// sidKey saves the session ID.
|
||||
sidKey struct{}
|
||||
)
|
||||
|
||||
func (s Sid) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func ContextWithSid(ctx context.Context, sid Sid) context.Context {
|
||||
return context.WithValue(ctx, keySid, sid)
|
||||
return context.WithValue(ctx, sidKey{}, sid)
|
||||
}
|
||||
|
||||
func SidFromContext(ctx context.Context) Sid {
|
||||
v, _ := ctx.Value(keySid).(Sid)
|
||||
v, _ := ctx.Value(sidKey{}).(Sid)
|
||||
return v
|
||||
}
|
||||
|
||||
// hashKey saves the hash source for Selector.
|
||||
type hashKey struct{}
|
||||
|
||||
type Hash struct {
|
||||
Source string
|
||||
}
|
||||
|
||||
var (
|
||||
clientHashKey = &hashKey{}
|
||||
type (
|
||||
// hashKey saves the hash source for Selector.
|
||||
hashKey struct{}
|
||||
Hash struct {
|
||||
Source string
|
||||
}
|
||||
)
|
||||
|
||||
func ContextWithHash(ctx context.Context, hash *Hash) context.Context {
|
||||
return context.WithValue(ctx, clientHashKey, hash)
|
||||
return context.WithValue(ctx, hashKey{}, hash)
|
||||
}
|
||||
|
||||
func HashFromContext(ctx context.Context) *Hash {
|
||||
if v, _ := ctx.Value(clientHashKey).(*Hash); v != nil {
|
||||
if v, _ := ctx.Value(hashKey{}).(*Hash); v != nil {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type clientIDKey struct{}
|
||||
type ClientID string
|
||||
|
||||
var (
|
||||
keyClientID = &clientIDKey{}
|
||||
type (
|
||||
ClientID string
|
||||
clientIDKey struct{}
|
||||
)
|
||||
|
||||
func (s ClientID) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
func ContextWithClientID(ctx context.Context, clientID ClientID) context.Context {
|
||||
return context.WithValue(ctx, keyClientID, clientID)
|
||||
return context.WithValue(ctx, clientIDKey{}, clientID)
|
||||
}
|
||||
|
||||
func ClientIDFromContext(ctx context.Context) ClientID {
|
||||
v, _ := ctx.Value(keyClientID).(ClientID)
|
||||
v, _ := ctx.Value(clientIDKey{}).(ClientID)
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
xdtls "github.com/go-gost/x/internal/util/dtls"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/pion/dtls/v2"
|
||||
@@ -50,6 +52,12 @@ func (d *dtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn = proxyproto.WrapClientConn(
|
||||
d.options.ProxyProtocol,
|
||||
xctx.SrcAddrFromContext(ctx),
|
||||
xctx.DstAddrFromContext(ctx),
|
||||
conn)
|
||||
|
||||
tlsCfg := d.options.TLSConfig
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &tls.Config{
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+14
-1
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/go-gost/core/dialer"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
pb "github.com/go-gost/x/internal/util/grpc/proto"
|
||||
"github.com/go-gost/x/registry"
|
||||
"google.golang.org/grpc"
|
||||
@@ -71,7 +73,18 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
grpcOpts := []grpc.DialOption{
|
||||
// grpc.WithBlock(),
|
||||
grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {
|
||||
return options.Dialer.Dial(c, "tcp", s)
|
||||
conn, err := options.Dialer.Dial(c, "tcp", s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn = proxyproto.WrapClientConn(
|
||||
d.options.ProxyProtocol,
|
||||
xctx.SrcAddrFromContext(ctx),
|
||||
xctx.DstAddrFromContext(ctx),
|
||||
conn)
|
||||
|
||||
return conn, nil
|
||||
}),
|
||||
grpc.WithAuthority(host),
|
||||
grpc.WithConnectParams(grpc.ConnectParams{
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
)
|
||||
|
||||
// a dummy HTTP2 client conn used by HTTP2 client connector
|
||||
@@ -13,7 +12,7 @@ type conn struct {
|
||||
localAddr net.Addr
|
||||
remoteAddr net.Addr
|
||||
onClose func()
|
||||
md mdata.Metadata
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
@@ -51,7 +50,6 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
|
||||
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||
}
|
||||
|
||||
// Metadata implements metadata.Metadatable interface.
|
||||
func (c *conn) Metadata() mdata.Metadata {
|
||||
return c.md
|
||||
func (c *conn) Context() context.Context {
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
+18
-7
@@ -10,7 +10,10 @@ import (
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
ictx "github.com/go-gost/x/internal/ctx"
|
||||
net_dialer "github.com/go-gost/x/internal/net/dialer"
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
mdx "github.com/go-gost/x/metadata"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
@@ -23,7 +26,6 @@ type http2Dialer struct {
|
||||
clients map[string]*http.Client
|
||||
clientMutex sync.Mutex
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options dialer.Options
|
||||
}
|
||||
|
||||
@@ -91,7 +93,18 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
|
||||
if netd == nil {
|
||||
netd = net_dialer.DefaultNetDialer
|
||||
}
|
||||
return netd.Dial(ctx, network, addr)
|
||||
conn, err := netd.Dial(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn = proxyproto.WrapClientConn(
|
||||
d.options.ProxyProtocol,
|
||||
xctx.SrcAddrFromContext(ctx),
|
||||
xctx.DstAddrFromContext(ctx),
|
||||
conn)
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 16,
|
||||
@@ -103,7 +116,7 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
|
||||
d.clients[address] = client
|
||||
}
|
||||
|
||||
var c net.Conn = &conn{
|
||||
return &conn{
|
||||
localAddr: &net.TCPAddr{},
|
||||
remoteAddr: raddr,
|
||||
onClose: func() {
|
||||
@@ -111,8 +124,6 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
|
||||
defer d.clientMutex.Unlock()
|
||||
delete(d.clients, address)
|
||||
},
|
||||
md: mdx.NewMetadata(map[string]any{"client": client}),
|
||||
}
|
||||
|
||||
return c, nil
|
||||
ctx: ictx.ContextWithMetadata(ctx, mdx.NewMetadata(map[string]any{"client": client})),
|
||||
}, nil
|
||||
}
|
||||
|
||||
+33
-12
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/go-gost/core/dialer"
|
||||
"github.com/go-gost/core/logger"
|
||||
md "github.com/go-gost/core/metadata"
|
||||
xctx "github.com/go-gost/x/ctx"
|
||||
"github.com/go-gost/x/internal/net/proxyproto"
|
||||
"github.com/go-gost/x/registry"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
@@ -93,15 +95,37 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
|
||||
if d.h2c {
|
||||
client.Transport = &http2.Transport{
|
||||
AllowHTTP: true,
|
||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
return options.Dialer.Dial(ctx, network, addr)
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
conn, err := options.Dialer.Dial(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn = proxyproto.WrapClientConn(
|
||||
d.options.ProxyProtocol,
|
||||
xctx.SrcAddrFromContext(ctx),
|
||||
xctx.DstAddrFromContext(ctx),
|
||||
conn)
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
}
|
||||
} else {
|
||||
client.Transport = &http.Transport{
|
||||
TLSClientConfig: d.options.TLSConfig,
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return options.Dialer.Dial(ctx, network, addr)
|
||||
conn, err := options.Dialer.Dial(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn = proxyproto.WrapClientConn(
|
||||
d.options.ProxyProtocol,
|
||||
xctx.SrcAddrFromContext(ctx),
|
||||
xctx.DstAddrFromContext(ctx),
|
||||
conn)
|
||||
|
||||
return conn, nil
|
||||
},
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
@@ -126,14 +150,11 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
req := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: scheme, Host: host},
|
||||
Header: d.md.header,
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Body: pr,
|
||||
Host: host,
|
||||
// ContentLength: -1,
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Scheme: scheme, Host: host},
|
||||
Header: d.md.header,
|
||||
Body: pr,
|
||||
Host: host,
|
||||
}
|
||||
if req.Header == nil {
|
||||
req.Header = make(http.Header)
|
||||
@@ -148,7 +169,7 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
|
||||
d.logger.Trace(string(dump))
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := client.Do(req.WithContext(context.WithoutCancel(ctx)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
mdata "github.com/go-gost/core/metadata"
|
||||
mdutil "github.com/go-gost/core/metadata/util"
|
||||
mdutil "github.com/go-gost/x/metadata/util"
|
||||
)
|
||||
|
||||
type metadata struct {
|
||||
|
||||
@@ -70,9 +70,9 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
|
||||
Host: host,
|
||||
Client: &http.Client{
|
||||
// Timeout: 60 * time.Second,
|
||||
Transport: &http3.RoundTripper{
|
||||
Transport: &http3.Transport{
|
||||
TLSClientConfig: d.options.TLSConfig,
|
||||
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
||||
// d.options.Logger.Infof("dial: %s/%s, %s", addr, network, host)
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
@@ -90,7 +90,7 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
|
||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||
Versions: []quic.VersionNumber{
|
||||
Versions: []quic.Version{
|
||||
quic.Version1,
|
||||
},
|
||||
MaxIncomingStreams: int64(d.md.maxStreams),
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package masque
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
)
|
||||
|
||||
// Client manages HTTP/3 connections for MASQUE proxying.
|
||||
// It wraps a QUIC connection and HTTP/3 client, following the session pattern
|
||||
// used by other gost dialers (ssh, quic, mws, etc.).
|
||||
type Client struct {
|
||||
host string
|
||||
addr string
|
||||
transport *http3.Transport
|
||||
quicConn *quic.Conn
|
||||
clientConn *http3.ClientConn
|
||||
dialer func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error)
|
||||
log logger.Logger
|
||||
}
|
||||
|
||||
// IsClosed returns true if the underlying connection is closed.
|
||||
// This follows the pattern used by other gost session wrappers.
|
||||
func (c *Client) IsClosed() bool {
|
||||
if c.clientConn == nil {
|
||||
return true
|
||||
}
|
||||
select {
|
||||
case <-c.clientConn.Context().Done():
|
||||
return true
|
||||
default:
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Close closes the client connection.
|
||||
func (c *Client) Close() error {
|
||||
if c.quicConn != nil {
|
||||
return (*c.quicConn).CloseWithError(0, "closed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dial establishes an HTTP/3 connection to the proxy server.
|
||||
// It returns a MasqueConn that can be used by the MASQUE connector.
|
||||
// Following the QUIC dialer pattern, this opens a request stream immediately
|
||||
// to detect dead connections and allow cache invalidation.
|
||||
func (c *Client) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
// Create connection if we don't have one (caller checks IsClosed first)
|
||||
if c.clientConn == nil {
|
||||
// Use the transport's Dial function to establish QUIC connection
|
||||
quicConn, err := c.dialer(ctx, addr, c.transport.TLSClientConfig, c.transport.QUICConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.quicConn = quicConn
|
||||
|
||||
// Create HTTP/3 client connection on top of QUIC
|
||||
c.clientConn = c.transport.NewClientConn(quicConn)
|
||||
}
|
||||
|
||||
// Open request stream NOW (following QUIC dialer pattern).
|
||||
// This allows dead connection detection - if this fails, caller can invalidate cache.
|
||||
reqStream, err := c.clientConn.OpenRequestStream(ctx)
|
||||
if err != nil {
|
||||
// Connection is dead - clear our state so next dial creates fresh connection
|
||||
c.clientConn = nil
|
||||
c.quicConn = nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return a connection wrapper with pre-opened stream
|
||||
return &MasqueConn{
|
||||
clientConn: c.clientConn,
|
||||
reqStream: reqStream,
|
||||
host: c.host,
|
||||
log: c.log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MasqueConn wraps an HTTP/3 client connection for use with the MASQUE connector.
|
||||
// It implements net.Conn for compatibility with the gost connector interface,
|
||||
// but the actual data transfer happens via datagrams through the pre-opened RequestStream.
|
||||
type MasqueConn struct {
|
||||
clientConn *http3.ClientConn
|
||||
reqStream *http3.RequestStream // Pre-opened stream for CONNECT-UDP
|
||||
host string
|
||||
log logger.Logger
|
||||
}
|
||||
|
||||
// GetRequestStream returns the pre-opened HTTP/3 request stream.
|
||||
// The connector uses this for the CONNECT-UDP handshake.
|
||||
func (c *MasqueConn) GetRequestStream() *http3.RequestStream {
|
||||
return c.reqStream
|
||||
}
|
||||
|
||||
// GetHost returns the proxy host.
|
||||
func (c *MasqueConn) GetHost() string {
|
||||
return c.host
|
||||
}
|
||||
|
||||
// Read implements net.Conn but is not used for MASQUE.
|
||||
// The actual data transfer happens via datagrams.
|
||||
func (c *MasqueConn) Read(b []byte) (n int, err error) {
|
||||
// This should not be called - datagrams are used for data transfer
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Write implements net.Conn but is not used for MASQUE.
|
||||
// The actual data transfer happens via datagrams.
|
||||
func (c *MasqueConn) Write(b []byte) (n int, err error) {
|
||||
// This should not be called - datagrams are used for data transfer
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *MasqueConn) Close() error {
|
||||
// Don't close the underlying clientConn as it's shared/pooled
|
||||
return nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (c *MasqueConn) LocalAddr() net.Addr {
|
||||
return &net.UDPAddr{}
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (c *MasqueConn) RemoteAddr() net.Addr {
|
||||
return &net.UDPAddr{}
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines.
|
||||
func (c *MasqueConn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline.
|
||||
func (c *MasqueConn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline.
|
||||
func (c *MasqueConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user