mirror of
https://github.com/ICKelin/opennotr.git
synced 2025-09-26 20:01:13 +08:00
147 lines
3.3 KiB
Go
147 lines
3.3 KiB
Go
package plugin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/ICKelin/opennotr/internal/logs"
|
|
)
|
|
|
|
var pluginMgr = &PluginManager{
|
|
routes: make(map[string]*PluginMeta),
|
|
plugins: make(map[string]IPlugin),
|
|
}
|
|
|
|
// ProxyTuple defineds plugins real proxy address
|
|
type ProxyTuple struct {
|
|
Protocol string
|
|
FromPort string
|
|
ToPort string
|
|
}
|
|
|
|
// PluginMeta defineds data that the plugins needs
|
|
// these members are filled by server.go
|
|
type PluginMeta struct {
|
|
// plugin register protocol
|
|
// eg: tcp, udp, http, http2, h2c
|
|
Protocol string
|
|
|
|
// From specific local listener address of plugin
|
|
// browser or other clients will connect to this address
|
|
// it's no use for restyproxy plugin.
|
|
From string
|
|
|
|
// To specific VIP:port of our VPN peer node.
|
|
// For example:
|
|
// our VPN virtual lan cidr is 100.64.100.1/24
|
|
// the connected VPN client's VPN lan ip is 100.64.100.10/24
|
|
// and it wants to export 8080 as http port, so the $To is
|
|
// 100.64.100.10:8080
|
|
To string
|
|
|
|
// Domain specific the domain of our VPN peer node.
|
|
// It could be empty
|
|
Domain string
|
|
|
|
// Data you want to passto plugin
|
|
// Reserve
|
|
Ctx interface{}
|
|
RecycleSignal chan struct{}
|
|
}
|
|
|
|
func (item *PluginMeta) identify() string {
|
|
return fmt.Sprintf("%s:%s:%s", item.Protocol, item.From, item.Domain)
|
|
}
|
|
|
|
// IPlugin defines plugin interface
|
|
// Plugin should implements the IPlugin
|
|
type IPlugin interface {
|
|
// Setup calls at the begin of plugin system initialize
|
|
// plugin system will pass the raw message to plugin's Setup function
|
|
Setup(json.RawMessage) error
|
|
|
|
// Close a proxy, it may be called by client's connection close
|
|
StopProxy(item *PluginMeta)
|
|
|
|
// Run a proxy, it may be called by client's connection established
|
|
RunProxy(item *PluginMeta) (*ProxyTuple, error)
|
|
}
|
|
|
|
type PluginManager struct {
|
|
mu sync.Mutex
|
|
|
|
// routes stores proxier of localAddress
|
|
// key: pluginMeta.identify()
|
|
// value: pluginMeta
|
|
routes map[string]*PluginMeta
|
|
|
|
// plugins store plugin information
|
|
// by call plugin.Register function.
|
|
// key: protocol, eg: tcp, udp
|
|
// value: plugin implement
|
|
plugins map[string]IPlugin
|
|
}
|
|
|
|
func DefaultPluginManager() *PluginManager {
|
|
return pluginMgr
|
|
}
|
|
|
|
func Register(protocol string, p IPlugin) {
|
|
pluginMgr.plugins[protocol] = p
|
|
}
|
|
|
|
func Setup(plugins map[string]string) error {
|
|
for protocol, cfg := range plugins {
|
|
logs.Info("setup for %s with configuration:\n%s", protocol, cfg)
|
|
plug, ok := pluginMgr.plugins[protocol]
|
|
if !ok {
|
|
logs.Error("protocol %s not register", protocol)
|
|
return fmt.Errorf("protocol %s not register", protocol)
|
|
}
|
|
|
|
err := plug.Setup([]byte(cfg))
|
|
if err != nil {
|
|
logs.Error("setup protocol %s fail: %v", protocol, err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *PluginManager) AddProxy(item *PluginMeta) (*ProxyTuple, error) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
key := item.identify()
|
|
if _, ok := p.routes[key]; ok {
|
|
return nil, fmt.Errorf("port %s is in used", key)
|
|
}
|
|
|
|
plug, ok := p.plugins[item.Protocol]
|
|
if !ok {
|
|
return nil, fmt.Errorf("proxy %s not register", item.Protocol)
|
|
}
|
|
|
|
tuple, err := plug.RunProxy(item)
|
|
if err != nil {
|
|
logs.Error("run proxy fail: %v", err)
|
|
return nil, err
|
|
}
|
|
p.routes[key] = item
|
|
return tuple, nil
|
|
}
|
|
|
|
func (p *PluginManager) DelProxy(item *PluginMeta) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
key := item.identify()
|
|
|
|
plug, ok := p.plugins[item.Protocol]
|
|
if ok {
|
|
plug.StopProxy(item)
|
|
}
|
|
|
|
delete(p.routes, key)
|
|
}
|