update
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m54s

This commit is contained in:
Flik
2025-12-26 17:14:54 +08:00
parent 4623a7f031
commit 549f9aaf26
63 changed files with 10266 additions and 740 deletions

68
pkg/auth/jwt.go Normal file
View File

@@ -0,0 +1,68 @@
package auth
import (
"crypto/rand"
"encoding/hex"
"time"
"github.com/golang-jwt/jwt/v5"
)
// Claims JWT claims
type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
// JWTAuth JWT 认证管理器
type JWTAuth struct {
secret []byte
expiration time.Duration
}
// NewJWTAuth 创建 JWT 认证管理器
func NewJWTAuth(secret string, expHours int) *JWTAuth {
if secret == "" {
secret = generateSecret()
}
return &JWTAuth{
secret: []byte(secret),
expiration: time.Duration(expHours) * time.Hour,
}
}
// GenerateToken 生成 JWT token
func (j *JWTAuth) GenerateToken(username string) (string, error) {
claims := &Claims{
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.expiration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "gotunnel",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.secret)
}
// ValidateToken 验证 JWT token
func (j *JWTAuth) ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return j.secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, jwt.ErrSignatureInvalid
}
func generateSecret() string {
b := make([]byte, 32)
rand.Read(b)
return hex.EncodeToString(b)
}

View File

@@ -1,11 +1,6 @@
package plugin
// RegisterBuiltins 注册所有内置 plugins
// 注意:此函数需要在调用方导入 builtin 包并手动注册
// 示例:
// registry := plugin.NewRegistry()
// registry.RegisterBuiltin(builtin.NewSOCKS5Plugin())
// registry.RegisterBuiltin(builtin.NewHTTPPlugin())
func RegisterBuiltins(registry *Registry, handlers ...ProxyHandler) error {
for _, handler := range handlers {
if err := registry.RegisterBuiltin(handler); err != nil {

View File

@@ -0,0 +1,16 @@
package builtin
import "github.com/gotunnel/pkg/plugin"
// 全局插件注册表
var registry []plugin.ProxyHandler
// Register 插件自注册函数,由各插件的 init() 调用
func Register(handler plugin.ProxyHandler) {
registry = append(registry, handler)
}
// GetAll 返回所有已注册的内置插件
func GetAll() []plugin.ProxyHandler {
return registry
}

View File

@@ -10,6 +10,10 @@ import (
"github.com/gotunnel/pkg/plugin"
)
func init() {
Register(NewSOCKS5Plugin())
}
const (
socks5Version = 0x05
noAuth = 0x00

86
pkg/plugin/builtin/vnc.go Normal file
View File

@@ -0,0 +1,86 @@
package builtin
import (
"io"
"log"
"net"
"github.com/gotunnel/pkg/plugin"
)
func init() {
Register(NewVNCPlugin())
}
// VNCPlugin VNC 远程桌面插件
type VNCPlugin struct {
config map[string]string
}
// NewVNCPlugin 创建 VNC plugin
func NewVNCPlugin() *VNCPlugin {
return &VNCPlugin{}
}
// Metadata 返回 plugin 信息
func (p *VNCPlugin) Metadata() plugin.PluginMetadata {
return plugin.PluginMetadata{
Name: "vnc",
Version: "1.0.0",
Type: plugin.PluginTypeApp,
Source: plugin.PluginSourceBuiltin,
Description: "VNC remote desktop relay (connects to client's local VNC server)",
Author: "GoTunnel",
Capabilities: []string{
"dial", "read", "write", "close",
},
}
}
// Init 初始化 plugin
func (p *VNCPlugin) Init(config map[string]string) error {
p.config = config
return nil
}
// HandleConn 处理 VNC 连接
// 将外部 VNC 客户端连接转发到客户端本地的 VNC 服务
func (p *VNCPlugin) HandleConn(conn net.Conn, dialer plugin.Dialer) error {
defer conn.Close()
// 默认连接客户端本地的 VNC 服务 (5900)
vncAddr := "127.0.0.1:5900"
if addr, ok := p.config["vnc_addr"]; ok && addr != "" {
vncAddr = addr
}
log.Printf("[VNC] New connection from %s, forwarding to %s", conn.RemoteAddr(), vncAddr)
// 通过隧道连接到客户端本地的 VNC 服务
remote, err := dialer.Dial("tcp", vncAddr)
if err != nil {
log.Printf("[VNC] Failed to connect to %s: %v", vncAddr, err)
return err
}
defer remote.Close()
// 双向转发 VNC 流量
errCh := make(chan error, 2)
go func() {
_, err := io.Copy(remote, conn)
errCh <- err
}()
go func() {
_, err := io.Copy(conn, remote)
errCh <- err
}()
// 等待任一方向完成
<-errCh
return nil
}
// Close 释放资源
func (p *VNCPlugin) Close() error {
return nil
}

View File

@@ -8,14 +8,16 @@ import (
// Registry 管理可用的 plugins
type Registry struct {
builtin map[string]ProxyHandler // 内置 Go 实现
mu sync.RWMutex
builtin map[string]ProxyHandler // 内置 Go 实现
enabled map[string]bool // 启用状态
mu sync.RWMutex
}
// NewRegistry 创建 plugin 注册表
func NewRegistry() *Registry {
return &Registry{
builtin: make(map[string]ProxyHandler),
enabled: make(map[string]bool),
}
}
@@ -34,6 +36,7 @@ func (r *Registry) RegisterBuiltin(handler ProxyHandler) error {
}
r.builtin[meta.Name] = handler
r.enabled[meta.Name] = true // 默认启用
return nil
}
@@ -44,6 +47,9 @@ func (r *Registry) Get(proxyType string) (ProxyHandler, error) {
// 先查找内置 plugin
if handler, ok := r.builtin[proxyType]; ok {
if !r.enabled[proxyType] {
return nil, fmt.Errorf("plugin %s is disabled", proxyType)
}
return handler, nil
}
@@ -58,10 +64,11 @@ func (r *Registry) List() []PluginInfo {
var plugins []PluginInfo
// 内置 plugins
for _, handler := range r.builtin {
for name, handler := range r.builtin {
plugins = append(plugins, PluginInfo{
Metadata: handler.Metadata(),
Loaded: true,
Enabled: r.enabled[name],
})
}
@@ -91,3 +98,44 @@ func (r *Registry) Close(ctx context.Context) error {
return lastErr
}
// Enable 启用插件
func (r *Registry) Enable(name string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.builtin[name]; !ok {
return fmt.Errorf("plugin %s not found", name)
}
r.enabled[name] = true
return nil
}
// Disable 禁用插件
func (r *Registry) Disable(name string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.builtin[name]; !ok {
return fmt.Errorf("plugin %s not found", name)
}
r.enabled[name] = false
return nil
}
// IsEnabled 检查插件是否启用
func (r *Registry) IsEnabled(name string) bool {
r.mu.RLock()
defer r.mu.RUnlock()
return r.enabled[name]
}
// RegisterAll 批量注册插件
func (r *Registry) RegisterAll(handlers []ProxyHandler) error {
for _, handler := range handlers {
if err := r.RegisterBuiltin(handler); err != nil {
return err
}
}
return nil
}

View File

@@ -1,29 +0,0 @@
package store
import (
"github.com/gotunnel/pkg/plugin"
)
// PluginStore 管理 plugin 持久化
type PluginStore interface {
// GetAllPlugins 返回所有存储的 plugins
GetAllPlugins() ([]plugin.PluginMetadata, error)
// GetPlugin 返回指定 plugin 的元数据
GetPlugin(name string) (*plugin.PluginMetadata, error)
// GetPluginData 返回 WASM 二进制
GetPluginData(name string) ([]byte, error)
// SavePlugin 存储 plugin
SavePlugin(metadata plugin.PluginMetadata, wasmData []byte) error
// DeletePlugin 删除 plugin
DeletePlugin(name string) error
// PluginExists 检查 plugin 是否存在
PluginExists(name string) (bool, error)
// Close 关闭存储
Close() error
}

View File

@@ -1,168 +0,0 @@
package store
import (
"database/sql"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gotunnel/pkg/plugin"
_ "modernc.org/sqlite"
)
// SQLiteStore SQLite 实现的 PluginStore
type SQLiteStore struct {
db *sql.DB
mu sync.RWMutex
}
// NewSQLiteStore 创建 SQLite plugin 存储
func NewSQLiteStore(dbPath string) (*SQLiteStore, error) {
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return nil, err
}
store := &SQLiteStore{db: db}
if err := store.init(); err != nil {
db.Close()
return nil, err
}
return store, nil
}
// init 初始化数据库表
func (s *SQLiteStore) init() error {
query := `
CREATE TABLE IF NOT EXISTS plugins (
name TEXT PRIMARY KEY,
version TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'proxy',
source TEXT NOT NULL DEFAULT 'wasm',
description TEXT,
author TEXT,
checksum TEXT NOT NULL,
size INTEGER NOT NULL,
capabilities TEXT NOT NULL DEFAULT '[]',
config_schema TEXT NOT NULL DEFAULT '{}',
wasm_data BLOB NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`
_, err := s.db.Exec(query)
return err
}
// GetAllPlugins 返回所有存储的 plugins
func (s *SQLiteStore) GetAllPlugins() ([]plugin.PluginMetadata, error) {
s.mu.RLock()
defer s.mu.RUnlock()
rows, err := s.db.Query(`
SELECT name, version, type, source, description, author,
checksum, size, capabilities, config_schema
FROM plugins`)
if err != nil {
return nil, err
}
defer rows.Close()
var plugins []plugin.PluginMetadata
for rows.Next() {
var m plugin.PluginMetadata
var capJSON, configJSON string
err := rows.Scan(&m.Name, &m.Version, &m.Type, &m.Source,
&m.Description, &m.Author, &m.Checksum, &m.Size,
&capJSON, &configJSON)
if err != nil {
return nil, err
}
json.Unmarshal([]byte(capJSON), &m.Capabilities)
json.Unmarshal([]byte(configJSON), &m.ConfigSchema)
plugins = append(plugins, m)
}
return plugins, rows.Err()
}
// GetPlugin 返回指定 plugin 的元数据
func (s *SQLiteStore) GetPlugin(name string) (*plugin.PluginMetadata, error) {
s.mu.RLock()
defer s.mu.RUnlock()
var m plugin.PluginMetadata
var capJSON, configJSON string
err := s.db.QueryRow(`
SELECT name, version, type, source, description, author,
checksum, size, capabilities, config_schema
FROM plugins WHERE name = ?`, name).Scan(
&m.Name, &m.Version, &m.Type, &m.Source,
&m.Description, &m.Author, &m.Checksum, &m.Size,
&capJSON, &configJSON)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
json.Unmarshal([]byte(capJSON), &m.Capabilities)
json.Unmarshal([]byte(configJSON), &m.ConfigSchema)
return &m, nil
}
// GetPluginData 返回 WASM 二进制
func (s *SQLiteStore) GetPluginData(name string) ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
var data []byte
err := s.db.QueryRow(`SELECT wasm_data FROM plugins WHERE name = ?`, name).Scan(&data)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("plugin %s not found", name)
}
return data, err
}
// SavePlugin 存储 plugin
func (s *SQLiteStore) SavePlugin(metadata plugin.PluginMetadata, wasmData []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
capJSON, _ := json.Marshal(metadata.Capabilities)
configJSON, _ := json.Marshal(metadata.ConfigSchema)
_, err := s.db.Exec(`
INSERT OR REPLACE INTO plugins
(name, version, type, source, description, author, checksum, size,
capabilities, config_schema, wasm_data, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
metadata.Name, metadata.Version, metadata.Type, metadata.Source,
metadata.Description, metadata.Author, metadata.Checksum, metadata.Size,
string(capJSON), string(configJSON), wasmData, time.Now())
return err
}
// DeletePlugin 删除 plugin
func (s *SQLiteStore) DeletePlugin(name string) error {
s.mu.Lock()
defer s.mu.Unlock()
_, err := s.db.Exec(`DELETE FROM plugins WHERE name = ?`, name)
return err
}
// PluginExists 检查 plugin 是否存在
func (s *SQLiteStore) PluginExists(name string) (bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()
var count int
err := s.db.QueryRow(`SELECT COUNT(*) FROM plugins WHERE name = ?`, name).Scan(&count)
return count > 0, err
}
// Close 关闭存储
func (s *SQLiteStore) Close() error {
return s.db.Close()
}

View File

@@ -9,7 +9,10 @@ import (
type PluginType string
const (
PluginTypeProxy PluginType = "proxy" // 代理处理器 (SOCKS5, HTTP 等)
PluginTypeProxy PluginType = "proxy" // 代理协议插件 (SOCKS5 等)
PluginTypeApp PluginType = "app" // 应用插件 (VNC, 文件管理等)
PluginTypeService PluginType = "service" // 服务插件 (Web服务等)
PluginTypeTool PluginType = "tool" // 工具插件 (监控、日志等)
)
// PluginSource 表示 plugin 来源
@@ -38,6 +41,7 @@ type PluginMetadata struct {
type PluginInfo struct {
Metadata PluginMetadata `json:"metadata"`
Loaded bool `json:"loaded"`
Enabled bool `json:"enabled"`
LoadedAt time.Time `json:"loaded_at,omitempty"`
Error string `json:"error,omitempty"`
}

View File

@@ -34,6 +34,9 @@ const (
// UDP 相关消息
MsgTypeUDPData uint8 = 30 // UDP 数据包
// 插件安装消息
MsgTypeInstallPlugins uint8 = 24 // 服务端推送安装插件列表
)
// Message 基础消息结构
@@ -50,8 +53,9 @@ type AuthRequest struct {
// AuthResponse 认证响应
type AuthResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Success bool `json:"success"`
Message string `json:"message"`
ClientID string `json:"client_id,omitempty"` // 服务端分配的客户端 ID
}
// ProxyRule 代理规则
@@ -137,6 +141,11 @@ type PluginReadyNotification struct {
Error string `json:"error,omitempty"`
}
// InstallPluginsRequest 安装插件请求
type InstallPluginsRequest struct {
Plugins []string `json:"plugins"` // 要安装的插件名称列表
}
// UDPPacket UDP 数据包
type UDPPacket struct {
RemotePort int `json:"remote_port"` // 服务端监听端口