111
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 29s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 48s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 37s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 59s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 36s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 57s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 38s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m6s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 50s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 35s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m0s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 52s

This commit is contained in:
Flik
2025-12-28 16:51:40 +08:00
parent abfc235357
commit 17f38f7ef2
11 changed files with 782 additions and 25 deletions

103
pkg/plugin/api.go Normal file
View File

@@ -0,0 +1,103 @@
package plugin
import (
"net"
"time"
)
// =============================================================================
// 核心接口定义 - 按职责分离
// =============================================================================
// Dialer 网络拨号接口(已在 types.go 中定义,此处为文档说明)
// type Dialer interface {
// Dial(network, address string) (net.Conn, error)
// }
// PortManager 端口管理接口(仅服务端可用)
type PortManager interface {
// ReservePort 预留端口,返回错误如果端口已被占用
ReservePort(port int) error
// ReleasePort 释放端口
ReleasePort(port int)
// IsPortAvailable 检查端口是否可用
IsPortAvailable(port int) bool
}
// RuleManager 代理规则管理接口(仅服务端可用)
type RuleManager interface {
// CreateRule 创建代理规则
CreateRule(rule *RuleConfig) error
// DeleteRule 删除代理规则
DeleteRule(clientID, ruleName string) error
// GetRules 获取客户端的代理规则
GetRules(clientID string) ([]RuleConfig, error)
// UpdateRule 更新代理规则
UpdateRule(clientID string, rule *RuleConfig) error
}
// ClientManager 客户端管理接口(仅服务端可用)
type ClientManager interface {
// GetClientList 获取所有客户端列表
GetClientList() ([]ClientInfo, error)
// IsClientOnline 检查客户端是否在线
IsClientOnline(clientID string) bool
}
// Logger 日志接口
type Logger interface {
// Log 记录日志
Log(level LogLevel, format string, args ...interface{})
}
// ConfigStore 配置存储接口
type ConfigStore interface {
// GetConfig 获取配置值
GetConfig(key string) string
// SetConfig 设置配置值
SetConfig(key, value string)
}
// EventBus 事件总线接口
type EventBus interface {
// OnEvent 订阅事件
OnEvent(eventType EventType, handler EventHandler)
// EmitEvent 发送事件
EmitEvent(event *Event)
}
// =============================================================================
// 组合接口
// =============================================================================
// PluginAPI 插件 API 主接口,组合所有子接口
// 插件可以通过此接口访问 GoTunnel 的功能
type PluginAPI interface {
// 网络操作
Dial(network, address string) (net.Conn, error)
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
Listen(network, address string) (net.Listener, error)
// 端口管理(服务端)
PortManager
// 规则管理(服务端)
RuleManager
// 客户端管理(服务端)
ClientManager
// 日志
Logger
// 配置
ConfigStore
// 事件
EventBus
// 上下文
GetContext() *Context
GetClientID() string
GetServerInfo() *ServerInfo
}

90
pkg/plugin/base.go Normal file
View File

@@ -0,0 +1,90 @@
package plugin
import (
"fmt"
"log"
"sync"
)
// =============================================================================
// 基础实现 - 提取公共代码
// =============================================================================
// baseAPI 包含服务端和客户端共享的基础功能
type baseAPI struct {
pluginName string
config map[string]string
configMu sync.RWMutex
eventHandlers map[EventType][]EventHandler
eventMu sync.RWMutex
}
// newBaseAPI 创建基础 API
func newBaseAPI(pluginName string, config map[string]string) *baseAPI {
cfg := config
if cfg == nil {
cfg = make(map[string]string)
}
return &baseAPI{
pluginName: pluginName,
config: cfg,
eventHandlers: make(map[EventType][]EventHandler),
}
}
// Log 记录日志
func (b *baseAPI) Log(level LogLevel, format string, args ...interface{}) {
prefix := fmt.Sprintf("[Plugin:%s] ", b.pluginName)
msg := fmt.Sprintf(format, args...)
log.Printf("%s%s", prefix, msg)
}
// GetConfig 获取配置值
func (b *baseAPI) GetConfig(key string) string {
b.configMu.RLock()
defer b.configMu.RUnlock()
return b.config[key]
}
// SetConfig 设置配置值
func (b *baseAPI) SetConfig(key, value string) {
b.configMu.Lock()
defer b.configMu.Unlock()
b.config[key] = value
}
// OnEvent 订阅事件
func (b *baseAPI) OnEvent(eventType EventType, handler EventHandler) {
b.eventMu.Lock()
defer b.eventMu.Unlock()
b.eventHandlers[eventType] = append(b.eventHandlers[eventType], handler)
}
// EmitEvent 发送事件(复制切片避免竞态条件)
func (b *baseAPI) EmitEvent(event *Event) {
b.eventMu.RLock()
handlers := make([]EventHandler, len(b.eventHandlers[event.Type]))
copy(handlers, b.eventHandlers[event.Type])
b.eventMu.RUnlock()
for _, handler := range handlers {
go handler(event)
}
}
// getPluginName 获取插件名称
func (b *baseAPI) getPluginName() string {
return b.pluginName
}
// getConfigMap 获取配置副本
func (b *baseAPI) getConfigMap() map[string]string {
b.configMu.RLock()
defer b.configMu.RUnlock()
result := make(map[string]string, len(b.config))
for k, v := range b.config {
result[k] = v
}
return result
}

View File

@@ -50,6 +50,9 @@ func (p *SOCKS5Plugin) Metadata() plugin.PluginMetadata {
Capabilities: []string{
"dial", "read", "write", "close",
},
RuleSchema: &plugin.RuleSchema{
NeedsLocalAddr: false, // SOCKS5 不需要本地地址
},
ConfigSchema: []plugin.ConfigField{
{
Key: "auth",

View File

@@ -34,6 +34,18 @@ func (p *VNCPlugin) Metadata() plugin.PluginMetadata {
Capabilities: []string{
"dial", "read", "write", "close",
},
RuleSchema: &plugin.RuleSchema{
NeedsLocalAddr: false,
ExtraFields: []plugin.ConfigField{
{
Key: "vnc_addr",
Label: "VNC 地址",
Type: plugin.ConfigFieldString,
Default: "127.0.0.1:5900",
Description: "客户端本地 VNC 服务地址",
},
},
},
}
}

161
pkg/plugin/client_api.go Normal file
View File

@@ -0,0 +1,161 @@
package plugin
import (
"context"
"fmt"
"net"
"time"
)
// =============================================================================
// 客户端 API 实现
// =============================================================================
// ClientAPI 客户端 PluginAPI 实现
type ClientAPI struct {
*baseAPI
clientID string
dialer Dialer
}
// ClientAPIOption 客户端 API 配置选项
type ClientAPIOption struct {
PluginName string
ClientID string
Config map[string]string
Dialer Dialer
}
// NewClientAPI 创建客户端 API
func NewClientAPI(opt ClientAPIOption) *ClientAPI {
return &ClientAPI{
baseAPI: newBaseAPI(opt.PluginName, opt.Config),
clientID: opt.ClientID,
dialer: opt.Dialer,
}
}
// --- 网络操作 ---
// Dial 通过隧道建立连接
func (c *ClientAPI) Dial(network, address string) (net.Conn, error) {
if c.dialer == nil {
return nil, ErrNotConnected
}
return c.dialer.Dial(network, address)
}
// DialTimeout 带超时的连接(使用 context 避免 goroutine 泄漏)
func (c *ClientAPI) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
if c.dialer == nil {
return nil, ErrNotConnected
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
type result struct {
conn net.Conn
err error
}
ch := make(chan result, 1)
go func() {
conn, err := c.dialer.Dial(network, address)
select {
case ch <- result{conn, err}:
case <-ctx.Done():
if conn != nil {
conn.Close()
}
}
}()
select {
case r := <-ch:
return r.conn, r.err
case <-ctx.Done():
return nil, fmt.Errorf("dial timeout")
}
}
// Listen 客户端不支持监听
func (c *ClientAPI) Listen(network, address string) (net.Listener, error) {
return nil, ErrNotSupported
}
// --- 端口管理(客户端不支持)---
// ReservePort 客户端不支持
func (c *ClientAPI) ReservePort(port int) error {
return ErrNotSupported
}
// ReleasePort 客户端不支持
func (c *ClientAPI) ReleasePort(port int) {}
// IsPortAvailable 检查本地端口是否可用
func (c *ClientAPI) IsPortAvailable(port int) bool {
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
return false
}
ln.Close()
return true
}
// --- 规则管理(客户端不支持)---
// CreateRule 客户端不支持
func (c *ClientAPI) CreateRule(rule *RuleConfig) error {
return ErrNotSupported
}
// DeleteRule 客户端不支持
func (c *ClientAPI) DeleteRule(clientID, ruleName string) error {
return ErrNotSupported
}
// GetRules 客户端不支持
func (c *ClientAPI) GetRules(clientID string) ([]RuleConfig, error) {
return nil, ErrNotSupported
}
// UpdateRule 客户端不支持
func (c *ClientAPI) UpdateRule(clientID string, rule *RuleConfig) error {
return ErrNotSupported
}
// --- 客户端管理 ---
// GetClientID 获取当前客户端 ID
func (c *ClientAPI) GetClientID() string {
return c.clientID
}
// GetClientList 客户端不支持
func (c *ClientAPI) GetClientList() ([]ClientInfo, error) {
return nil, ErrNotSupported
}
// IsClientOnline 客户端不支持
func (c *ClientAPI) IsClientOnline(clientID string) bool {
return false
}
// --- 上下文 ---
// GetContext 获取当前上下文
func (c *ClientAPI) GetContext() *Context {
return &Context{
PluginName: c.getPluginName(),
Side: SideClient,
ClientID: c.clientID,
Config: c.getConfigMap(),
}
}
// GetServerInfo 客户端不支持
func (c *ClientAPI) GetServerInfo() *ServerInfo {
return nil
}

180
pkg/plugin/server_api.go Normal file
View File

@@ -0,0 +1,180 @@
package plugin
import (
"net"
"time"
)
// =============================================================================
// 服务端依赖接口(依赖注入)
// =============================================================================
// PortStore 端口存储接口
type PortStore interface {
Reserve(port int, owner string) error
Release(port int)
IsAvailable(port int) bool
}
// RuleStore 规则存储接口
type RuleStore interface {
GetAll(clientID string) ([]RuleConfig, error)
Create(clientID string, rule *RuleConfig) error
Update(clientID string, rule *RuleConfig) error
Delete(clientID, ruleName string) error
}
// ClientStore 客户端存储接口
type ClientStore interface {
GetAll() ([]ClientInfo, error)
IsOnline(clientID string) bool
}
// =============================================================================
// 服务端 API 实现
// =============================================================================
// ServerAPI 服务端 PluginAPI 实现
type ServerAPI struct {
*baseAPI
portStore PortStore
ruleStore RuleStore
clientStore ClientStore
serverInfo *ServerInfo
}
// ServerAPIOption 服务端 API 配置选项
type ServerAPIOption struct {
PluginName string
Config map[string]string
PortStore PortStore
RuleStore RuleStore
ClientStore ClientStore
ServerInfo *ServerInfo
}
// NewServerAPI 创建服务端 API
func NewServerAPI(opt ServerAPIOption) *ServerAPI {
return &ServerAPI{
baseAPI: newBaseAPI(opt.PluginName, opt.Config),
portStore: opt.PortStore,
ruleStore: opt.RuleStore,
clientStore: opt.ClientStore,
serverInfo: opt.ServerInfo,
}
}
// --- 网络操作 ---
// Dial 服务端不支持隧道拨号
func (s *ServerAPI) Dial(network, address string) (net.Conn, error) {
return nil, ErrNotSupported
}
// DialTimeout 服务端不支持隧道拨号
func (s *ServerAPI) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return nil, ErrNotSupported
}
// Listen 在指定地址监听
func (s *ServerAPI) Listen(network, address string) (net.Listener, error) {
return net.Listen(network, address)
}
// --- 端口管理 ---
// ReservePort 预留端口
func (s *ServerAPI) ReservePort(port int) error {
if s.portStore == nil {
return ErrNotSupported
}
return s.portStore.Reserve(port, s.getPluginName())
}
// ReleasePort 释放端口
func (s *ServerAPI) ReleasePort(port int) {
if s.portStore != nil {
s.portStore.Release(port)
}
}
// IsPortAvailable 检查端口是否可用
func (s *ServerAPI) IsPortAvailable(port int) bool {
if s.portStore == nil {
return false
}
return s.portStore.IsAvailable(port)
}
// --- 规则管理 ---
// CreateRule 创建代理规则
func (s *ServerAPI) CreateRule(rule *RuleConfig) error {
if s.ruleStore == nil {
return ErrNotSupported
}
return s.ruleStore.Create(rule.ClientID, rule)
}
// DeleteRule 删除代理规则
func (s *ServerAPI) DeleteRule(clientID, ruleName string) error {
if s.ruleStore == nil {
return ErrNotSupported
}
return s.ruleStore.Delete(clientID, ruleName)
}
// GetRules 获取客户端的代理规则
func (s *ServerAPI) GetRules(clientID string) ([]RuleConfig, error) {
if s.ruleStore == nil {
return nil, ErrNotSupported
}
return s.ruleStore.GetAll(clientID)
}
// UpdateRule 更新代理规则
func (s *ServerAPI) UpdateRule(clientID string, rule *RuleConfig) error {
if s.ruleStore == nil {
return ErrNotSupported
}
return s.ruleStore.Update(clientID, rule)
}
// --- 客户端管理 ---
// GetClientID 服务端返回空
func (s *ServerAPI) GetClientID() string {
return ""
}
// GetClientList 获取所有客户端列表
func (s *ServerAPI) GetClientList() ([]ClientInfo, error) {
if s.clientStore == nil {
return nil, ErrNotSupported
}
return s.clientStore.GetAll()
}
// IsClientOnline 检查客户端是否在线
func (s *ServerAPI) IsClientOnline(clientID string) bool {
if s.clientStore == nil {
return false
}
return s.clientStore.IsOnline(clientID)
}
// --- 上下文 ---
// GetContext 获取当前上下文
func (s *ServerAPI) GetContext() *Context {
return &Context{
PluginName: s.getPluginName(),
Side: SideServer,
Config: s.getConfigMap(),
}
}
// GetServerInfo 获取服务端信息
func (s *ServerAPI) GetServerInfo() *ServerInfo {
return s.serverInfo
}

View File

@@ -45,6 +45,12 @@ type ConfigField struct {
Description string `json:"description,omitempty"` // 字段描述
}
// RuleSchema 规则表单模式定义
type RuleSchema struct {
NeedsLocalAddr bool `json:"needs_local_addr"` // 是否需要本地地址
ExtraFields []ConfigField `json:"extra_fields,omitempty"` // 额外字段
}
// PluginMetadata 描述一个 plugin
type PluginMetadata struct {
Name string `json:"name"` // 唯一标识符 (如 "socks5")
@@ -57,7 +63,8 @@ type PluginMetadata struct {
Checksum string `json:"checksum,omitempty"` // WASM 二进制的 SHA256
Size int64 `json:"size,omitempty"` // WASM 二进制大小
Capabilities []string `json:"capabilities,omitempty"` // 所需 host functions
ConfigSchema []ConfigField `json:"config_schema,omitempty"`// 配置模式定义
ConfigSchema []ConfigField `json:"config_schema,omitempty"`// 插件配置模式
RuleSchema *RuleSchema `json:"rule_schema,omitempty"` // 规则表单模式
}
// PluginInfo 组合元数据和运行时状态
@@ -90,6 +97,15 @@ type ProxyHandler interface {
Close() error
}
// ExtendedProxyHandler 扩展的代理处理器接口
// 支持 PluginAPI 的插件应实现此接口
type ExtendedProxyHandler interface {
ProxyHandler
// SetAPI 设置 PluginAPI允许插件调用系统功能
SetAPI(api PluginAPI)
}
// LogLevel 日志级别
type LogLevel uint8
@@ -100,6 +116,101 @@ const (
LogError
)
// =============================================================================
// API 相关类型
// =============================================================================
// Side 运行侧
type Side string
const (
SideServer Side = "server"
SideClient Side = "client"
)
// Context 插件运行上下文
type Context struct {
PluginName string
Side Side
ClientID string
Config map[string]string
}
// ServerInfo 服务端信息
type ServerInfo struct {
BindAddr string
BindPort int
Version string
}
// RuleConfig 代理规则配置
type RuleConfig struct {
ClientID string `json:"client_id"`
Name string `json:"name"`
Type string `json:"type"`
LocalIP string `json:"local_ip"`
LocalPort int `json:"local_port"`
RemotePort int `json:"remote_port"`
Enabled bool `json:"enabled"`
PluginName string `json:"plugin_name,omitempty"`
PluginConfig map[string]string `json:"plugin_config,omitempty"`
}
// ClientInfo 客户端信息
type ClientInfo struct {
ID string `json:"id"`
Nickname string `json:"nickname"`
Online bool `json:"online"`
LastPing string `json:"last_ping,omitempty"`
}
// EventType 事件类型
type EventType string
const (
EventClientConnect EventType = "client_connect"
EventClientDisconnect EventType = "client_disconnect"
EventRuleCreated EventType = "rule_created"
EventRuleDeleted EventType = "rule_deleted"
EventProxyConnect EventType = "proxy_connect"
EventProxyDisconnect EventType = "proxy_disconnect"
)
// Event 事件
type Event struct {
Type EventType `json:"type"`
Timestamp time.Time `json:"timestamp"`
Data map[string]interface{} `json:"data"`
}
// EventHandler 事件处理函数
type EventHandler func(event *Event)
// =============================================================================
// 错误定义
// =============================================================================
// APIError API 错误
type APIError struct {
Code int
Message string
}
func (e *APIError) Error() string {
return e.Message
}
// 常见 API 错误
var (
ErrNotSupported = &APIError{Code: 1, Message: "operation not supported"}
ErrClientNotFound = &APIError{Code: 2, Message: "client not found"}
ErrPortOccupied = &APIError{Code: 3, Message: "port already occupied"}
ErrRuleNotFound = &APIError{Code: 4, Message: "rule not found"}
ErrRuleExists = &APIError{Code: 5, Message: "rule already exists"}
ErrNotConnected = &APIError{Code: 6, Message: "not connected"}
ErrInvalidConfig = &APIError{Code: 7, Message: "invalid configuration"}
)
// ConnHandle WASM 连接句柄
type ConnHandle uint32