add plugins
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
All checks were successful
Build Multi-Platform Binaries / build (push) Successful in 11m9s
This commit is contained in:
16
pkg/plugin/builtin.go
Normal file
16
pkg/plugin/builtin.go
Normal file
@@ -0,0 +1,16 @@
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
116
pkg/plugin/builtin/http.go
Normal file
116
pkg/plugin/builtin/http.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
)
|
||||
|
||||
// HTTPPlugin 将现有 HTTP 代理实现封装为 plugin
|
||||
type HTTPPlugin struct {
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// NewHTTPPlugin 创建 HTTP plugin
|
||||
func NewHTTPPlugin() *HTTPPlugin {
|
||||
return &HTTPPlugin{}
|
||||
}
|
||||
|
||||
// Metadata 返回 plugin 信息
|
||||
func (p *HTTPPlugin) Metadata() plugin.PluginMetadata {
|
||||
return plugin.PluginMetadata{
|
||||
Name: "http",
|
||||
Version: "1.0.0",
|
||||
Type: plugin.PluginTypeProxy,
|
||||
Source: plugin.PluginSourceBuiltin,
|
||||
Description: "HTTP/HTTPS proxy protocol handler",
|
||||
Author: "GoTunnel",
|
||||
Capabilities: []string{
|
||||
"dial", "read", "write", "close",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Init 初始化 plugin
|
||||
func (p *HTTPPlugin) Init(config map[string]string) error {
|
||||
p.config = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleConn 处理 HTTP 代理连接
|
||||
func (p *HTTPPlugin) HandleConn(conn net.Conn, dialer plugin.Dialer) error {
|
||||
defer conn.Close()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
req, err := http.ReadRequest(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Method == http.MethodConnect {
|
||||
return p.handleConnect(conn, req, dialer)
|
||||
}
|
||||
return p.handleHTTP(conn, req, dialer)
|
||||
}
|
||||
|
||||
// Close 释放资源
|
||||
func (p *HTTPPlugin) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleConnect 处理 CONNECT 方法 (HTTPS)
|
||||
func (p *HTTPPlugin) handleConnect(conn net.Conn, req *http.Request, dialer plugin.Dialer) error {
|
||||
target := req.Host
|
||||
if !strings.Contains(target, ":") {
|
||||
target = target + ":443"
|
||||
}
|
||||
|
||||
remote, err := dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
conn.Write([]byte("HTTP/1.1 502 Bad Gateway\r\n\r\n"))
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n"))
|
||||
|
||||
go io.Copy(remote, conn)
|
||||
io.Copy(conn, remote)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleHTTP 处理普通 HTTP 请求
|
||||
func (p *HTTPPlugin) handleHTTP(conn net.Conn, req *http.Request, dialer plugin.Dialer) error {
|
||||
target := req.Host
|
||||
if !strings.Contains(target, ":") {
|
||||
target = target + ":80"
|
||||
}
|
||||
|
||||
remote, err := dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
conn.Write([]byte("HTTP/1.1 502 Bad Gateway\r\n\r\n"))
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
// 修改请求路径为相对路径
|
||||
req.URL.Scheme = ""
|
||||
req.URL.Host = ""
|
||||
req.RequestURI = req.URL.Path
|
||||
if req.URL.RawQuery != "" {
|
||||
req.RequestURI += "?" + req.URL.RawQuery
|
||||
}
|
||||
|
||||
// 发送请求到目标
|
||||
if err := req.Write(remote); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 转发响应
|
||||
_, err = io.Copy(conn, remote)
|
||||
return err
|
||||
}
|
||||
167
pkg/plugin/builtin/socks5.go
Normal file
167
pkg/plugin/builtin/socks5.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
)
|
||||
|
||||
const (
|
||||
socks5Version = 0x05
|
||||
noAuth = 0x00
|
||||
cmdConnect = 0x01
|
||||
atypIPv4 = 0x01
|
||||
atypDomain = 0x03
|
||||
atypIPv6 = 0x04
|
||||
)
|
||||
|
||||
// SOCKS5Plugin 将现有 SOCKS5 实现封装为 plugin
|
||||
type SOCKS5Plugin struct {
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// NewSOCKS5Plugin 创建 SOCKS5 plugin
|
||||
func NewSOCKS5Plugin() *SOCKS5Plugin {
|
||||
return &SOCKS5Plugin{}
|
||||
}
|
||||
|
||||
// Metadata 返回 plugin 信息
|
||||
func (p *SOCKS5Plugin) Metadata() plugin.PluginMetadata {
|
||||
return plugin.PluginMetadata{
|
||||
Name: "socks5",
|
||||
Version: "1.0.0",
|
||||
Type: plugin.PluginTypeProxy,
|
||||
Source: plugin.PluginSourceBuiltin,
|
||||
Description: "SOCKS5 proxy protocol handler (official plugin)",
|
||||
Author: "GoTunnel",
|
||||
Capabilities: []string{
|
||||
"dial", "read", "write", "close",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Init 初始化 plugin
|
||||
func (p *SOCKS5Plugin) Init(config map[string]string) error {
|
||||
p.config = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleConn 处理 SOCKS5 连接
|
||||
func (p *SOCKS5Plugin) HandleConn(conn net.Conn, dialer plugin.Dialer) error {
|
||||
defer conn.Close()
|
||||
|
||||
// 握手阶段
|
||||
if err := p.handshake(conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取请求
|
||||
target, err := p.readRequest(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 连接目标
|
||||
remote, err := dialer.Dial("tcp", target)
|
||||
if err != nil {
|
||||
p.sendReply(conn, 0x05) // Connection refused
|
||||
return err
|
||||
}
|
||||
defer remote.Close()
|
||||
|
||||
// 发送成功响应
|
||||
if err := p.sendReply(conn, 0x00); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 双向转发
|
||||
go io.Copy(remote, conn)
|
||||
io.Copy(conn, remote)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 释放资源
|
||||
func (p *SOCKS5Plugin) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// handshake 处理握手
|
||||
func (p *SOCKS5Plugin) handshake(conn net.Conn) error {
|
||||
buf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
if buf[0] != socks5Version {
|
||||
return errors.New("unsupported SOCKS version")
|
||||
}
|
||||
|
||||
nmethods := int(buf[1])
|
||||
methods := make([]byte, nmethods)
|
||||
if _, err := io.ReadFull(conn, methods); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 响应:使用无认证
|
||||
_, err := conn.Write([]byte{socks5Version, noAuth})
|
||||
return err
|
||||
}
|
||||
|
||||
// readRequest 读取请求
|
||||
func (p *SOCKS5Plugin) readRequest(conn net.Conn) (string, error) {
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if buf[0] != socks5Version || buf[1] != cmdConnect {
|
||||
return "", errors.New("unsupported command")
|
||||
}
|
||||
|
||||
var host string
|
||||
switch buf[3] {
|
||||
case atypIPv4:
|
||||
ip := make([]byte, 4)
|
||||
if _, err := io.ReadFull(conn, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = net.IP(ip).String()
|
||||
case atypDomain:
|
||||
lenBuf := make([]byte, 1)
|
||||
if _, err := io.ReadFull(conn, lenBuf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
domain := make([]byte, lenBuf[0])
|
||||
if _, err := io.ReadFull(conn, domain); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = string(domain)
|
||||
case atypIPv6:
|
||||
ip := make([]byte, 16)
|
||||
if _, err := io.ReadFull(conn, ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
host = net.IP(ip).String()
|
||||
default:
|
||||
return "", errors.New("unsupported address type")
|
||||
}
|
||||
|
||||
portBuf := make([]byte, 2)
|
||||
if _, err := io.ReadFull(conn, portBuf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
port := binary.BigEndian.Uint16(portBuf)
|
||||
|
||||
return fmt.Sprintf("%s:%d", host, port), nil
|
||||
}
|
||||
|
||||
// sendReply 发送响应
|
||||
func (p *SOCKS5Plugin) sendReply(conn net.Conn, rep byte) error {
|
||||
reply := []byte{socks5Version, rep, 0x00, atypIPv4, 0, 0, 0, 0, 0, 0}
|
||||
_, err := conn.Write(reply)
|
||||
return err
|
||||
}
|
||||
93
pkg/plugin/registry.go
Normal file
93
pkg/plugin/registry.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Registry 管理可用的 plugins
|
||||
type Registry struct {
|
||||
builtin map[string]ProxyHandler // 内置 Go 实现
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewRegistry 创建 plugin 注册表
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
builtin: make(map[string]ProxyHandler),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterBuiltin 注册内置 plugin
|
||||
func (r *Registry) RegisterBuiltin(handler ProxyHandler) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
meta := handler.Metadata()
|
||||
if meta.Name == "" {
|
||||
return fmt.Errorf("plugin name cannot be empty")
|
||||
}
|
||||
|
||||
if _, exists := r.builtin[meta.Name]; exists {
|
||||
return fmt.Errorf("plugin %s already registered", meta.Name)
|
||||
}
|
||||
|
||||
r.builtin[meta.Name] = handler
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 返回指定代理类型的 handler
|
||||
func (r *Registry) Get(proxyType string) (ProxyHandler, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
// 先查找内置 plugin
|
||||
if handler, ok := r.builtin[proxyType]; ok {
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("plugin %s not found", proxyType)
|
||||
}
|
||||
|
||||
// List 返回所有可用的 plugins
|
||||
func (r *Registry) List() []PluginInfo {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var plugins []PluginInfo
|
||||
|
||||
// 内置 plugins
|
||||
for _, handler := range r.builtin {
|
||||
plugins = append(plugins, PluginInfo{
|
||||
Metadata: handler.Metadata(),
|
||||
Loaded: true,
|
||||
})
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// Has 检查 plugin 是否存在
|
||||
func (r *Registry) Has(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
_, ok := r.builtin[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Close 关闭所有 plugins
|
||||
func (r *Registry) Close(ctx context.Context) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
var lastErr error
|
||||
for name, handler := range r.builtin {
|
||||
if err := handler.Close(); err != nil {
|
||||
lastErr = fmt.Errorf("failed to close plugin %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
29
pkg/plugin/store/interface.go
Normal file
29
pkg/plugin/store/interface.go
Normal file
@@ -0,0 +1,29 @@
|
||||
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
|
||||
}
|
||||
168
pkg/plugin/store/sqlite.go
Normal file
168
pkg/plugin/store/sqlite.go
Normal file
@@ -0,0 +1,168 @@
|
||||
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()
|
||||
}
|
||||
99
pkg/plugin/types.go
Normal file
99
pkg/plugin/types.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PluginType 定义 plugin 类别
|
||||
type PluginType string
|
||||
|
||||
const (
|
||||
PluginTypeProxy PluginType = "proxy" // 代理处理器 (SOCKS5, HTTP 等)
|
||||
)
|
||||
|
||||
// PluginSource 表示 plugin 来源
|
||||
type PluginSource string
|
||||
|
||||
const (
|
||||
PluginSourceBuiltin PluginSource = "builtin" // 内置编译
|
||||
PluginSourceWASM PluginSource = "wasm" // WASM 模块
|
||||
)
|
||||
|
||||
// PluginMetadata 描述一个 plugin
|
||||
type PluginMetadata struct {
|
||||
Name string `json:"name"` // 唯一标识符 (如 "socks5")
|
||||
Version string `json:"version"` // 语义化版本
|
||||
Type PluginType `json:"type"` // Plugin 类别
|
||||
Source PluginSource `json:"source"` // builtin 或 wasm
|
||||
Description string `json:"description"` // 人类可读描述
|
||||
Author string `json:"author"` // Plugin 作者
|
||||
Checksum string `json:"checksum,omitempty"` // WASM 二进制的 SHA256
|
||||
Size int64 `json:"size,omitempty"` // WASM 二进制大小
|
||||
Capabilities []string `json:"capabilities,omitempty"` // 所需 host functions
|
||||
ConfigSchema map[string]string `json:"config_schema,omitempty"`
|
||||
}
|
||||
|
||||
// PluginInfo 组合元数据和运行时状态
|
||||
type PluginInfo struct {
|
||||
Metadata PluginMetadata `json:"metadata"`
|
||||
Loaded bool `json:"loaded"`
|
||||
LoadedAt time.Time `json:"loaded_at,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Dialer 用于建立连接的接口
|
||||
type Dialer interface {
|
||||
Dial(network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// ProxyHandler 是所有 proxy plugin 必须实现的接口
|
||||
type ProxyHandler interface {
|
||||
// Metadata 返回 plugin 信息
|
||||
Metadata() PluginMetadata
|
||||
|
||||
// Init 使用配置初始化 plugin
|
||||
Init(config map[string]string) error
|
||||
|
||||
// HandleConn 处理传入连接
|
||||
// dialer 用于通过隧道建立连接
|
||||
HandleConn(conn net.Conn, dialer Dialer) error
|
||||
|
||||
// Close 释放 plugin 资源
|
||||
Close() error
|
||||
}
|
||||
|
||||
// LogLevel 日志级别
|
||||
type LogLevel uint8
|
||||
|
||||
const (
|
||||
LogDebug LogLevel = iota
|
||||
LogInfo
|
||||
LogWarn
|
||||
LogError
|
||||
)
|
||||
|
||||
// ConnHandle WASM 连接句柄
|
||||
type ConnHandle uint32
|
||||
|
||||
// HostContext 提供给 WASM plugin 的 host functions
|
||||
type HostContext interface {
|
||||
// 网络操作
|
||||
Dial(network, address string) (ConnHandle, error)
|
||||
Read(handle ConnHandle, buf []byte) (int, error)
|
||||
Write(handle ConnHandle, buf []byte) (int, error)
|
||||
CloseConn(handle ConnHandle) error
|
||||
|
||||
// 客户端连接操作
|
||||
ClientRead(buf []byte) (int, error)
|
||||
ClientWrite(buf []byte) (int, error)
|
||||
|
||||
// 日志
|
||||
Log(level LogLevel, message string)
|
||||
|
||||
// 时间
|
||||
Now() int64
|
||||
|
||||
// 配置
|
||||
GetConfig(key string) string
|
||||
}
|
||||
146
pkg/plugin/wasm/host.go
Normal file
146
pkg/plugin/wasm/host.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
)
|
||||
|
||||
// ErrInvalidHandle 无效的连接句柄
|
||||
var ErrInvalidHandle = errors.New("invalid connection handle")
|
||||
|
||||
// HostContextImpl 实现 HostContext 接口
|
||||
type HostContextImpl struct {
|
||||
dialer plugin.Dialer
|
||||
clientConn net.Conn
|
||||
config map[string]string
|
||||
|
||||
// 连接管理
|
||||
conns map[plugin.ConnHandle]net.Conn
|
||||
nextHandle plugin.ConnHandle
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewHostContext 创建 host context
|
||||
func NewHostContext(dialer plugin.Dialer, clientConn net.Conn, config map[string]string) *HostContextImpl {
|
||||
return &HostContextImpl{
|
||||
dialer: dialer,
|
||||
clientConn: clientConn,
|
||||
config: config,
|
||||
conns: make(map[plugin.ConnHandle]net.Conn),
|
||||
nextHandle: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial 通过隧道建立连接
|
||||
func (h *HostContextImpl) Dial(network, address string) (plugin.ConnHandle, error) {
|
||||
conn, err := h.dialer.Dial(network, address)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
h.mu.Lock()
|
||||
handle := h.nextHandle
|
||||
h.nextHandle++
|
||||
h.conns[handle] = conn
|
||||
h.mu.Unlock()
|
||||
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
// Read 从连接读取数据
|
||||
func (h *HostContextImpl) Read(handle plugin.ConnHandle, buf []byte) (int, error) {
|
||||
h.mu.Lock()
|
||||
conn, ok := h.conns[handle]
|
||||
h.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return 0, ErrInvalidHandle
|
||||
}
|
||||
|
||||
return conn.Read(buf)
|
||||
}
|
||||
|
||||
// Write 向连接写入数据
|
||||
func (h *HostContextImpl) Write(handle plugin.ConnHandle, buf []byte) (int, error) {
|
||||
h.mu.Lock()
|
||||
conn, ok := h.conns[handle]
|
||||
h.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return 0, ErrInvalidHandle
|
||||
}
|
||||
|
||||
return conn.Write(buf)
|
||||
}
|
||||
|
||||
// CloseConn 关闭连接
|
||||
func (h *HostContextImpl) CloseConn(handle plugin.ConnHandle) error {
|
||||
h.mu.Lock()
|
||||
conn, ok := h.conns[handle]
|
||||
if ok {
|
||||
delete(h.conns, handle)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return ErrInvalidHandle
|
||||
}
|
||||
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
// ClientRead 从客户端连接读取数据
|
||||
func (h *HostContextImpl) ClientRead(buf []byte) (int, error) {
|
||||
return h.clientConn.Read(buf)
|
||||
}
|
||||
|
||||
// ClientWrite 向客户端连接写入数据
|
||||
func (h *HostContextImpl) ClientWrite(buf []byte) (int, error) {
|
||||
return h.clientConn.Write(buf)
|
||||
}
|
||||
|
||||
// Log 记录日志
|
||||
func (h *HostContextImpl) Log(level plugin.LogLevel, message string) {
|
||||
prefix := "[WASM]"
|
||||
switch level {
|
||||
case plugin.LogDebug:
|
||||
prefix = "[WASM DEBUG]"
|
||||
case plugin.LogInfo:
|
||||
prefix = "[WASM INFO]"
|
||||
case plugin.LogWarn:
|
||||
prefix = "[WASM WARN]"
|
||||
case plugin.LogError:
|
||||
prefix = "[WASM ERROR]"
|
||||
}
|
||||
log.Printf("%s %s", prefix, message)
|
||||
}
|
||||
|
||||
// Now 返回当前 Unix 时间戳
|
||||
func (h *HostContextImpl) Now() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// GetConfig 获取配置值
|
||||
func (h *HostContextImpl) GetConfig(key string) string {
|
||||
if h.config == nil {
|
||||
return ""
|
||||
}
|
||||
return h.config[key]
|
||||
}
|
||||
|
||||
// Close 关闭所有连接
|
||||
func (h *HostContextImpl) Close() error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
for handle, conn := range h.conns {
|
||||
conn.Close()
|
||||
delete(h.conns, handle)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
29
pkg/plugin/wasm/memory.go
Normal file
29
pkg/plugin/wasm/memory.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
// ReadString 从 WASM 内存读取字符串
|
||||
func ReadString(mem api.Memory, ptr, len uint32) (string, bool) {
|
||||
data, ok := mem.Read(ptr, len)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
return string(data), true
|
||||
}
|
||||
|
||||
// WriteString 向 WASM 内存写入字符串
|
||||
func WriteString(mem api.Memory, ptr uint32, s string) bool {
|
||||
return mem.Write(ptr, []byte(s))
|
||||
}
|
||||
|
||||
// ReadBytes 从 WASM 内存读取字节
|
||||
func ReadBytes(mem api.Memory, ptr, len uint32) ([]byte, bool) {
|
||||
return mem.Read(ptr, len)
|
||||
}
|
||||
|
||||
// WriteBytes 向 WASM 内存写入字节
|
||||
func WriteBytes(mem api.Memory, ptr uint32, data []byte) bool {
|
||||
return mem.Write(ptr, data)
|
||||
}
|
||||
148
pkg/plugin/wasm/module.go
Normal file
148
pkg/plugin/wasm/module.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gotunnel/pkg/plugin"
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
// WASMPlugin 封装 WASM 模块作为 ProxyHandler
|
||||
type WASMPlugin struct {
|
||||
name string
|
||||
metadata plugin.PluginMetadata
|
||||
runtime *Runtime
|
||||
compiled wazero.CompiledModule
|
||||
config map[string]string
|
||||
}
|
||||
|
||||
// NewWASMPlugin 从 WASM 字节创建 plugin
|
||||
func NewWASMPlugin(ctx context.Context, rt *Runtime, name string, wasmBytes []byte) (*WASMPlugin, error) {
|
||||
compiled, err := rt.runtime.CompileModule(ctx, wasmBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("compile module: %w", err)
|
||||
}
|
||||
|
||||
p := &WASMPlugin{
|
||||
name: name,
|
||||
runtime: rt,
|
||||
compiled: compiled,
|
||||
}
|
||||
|
||||
// 尝试获取元数据
|
||||
if err := p.loadMetadata(ctx); err != nil {
|
||||
// 使用默认元数据
|
||||
p.metadata = plugin.PluginMetadata{
|
||||
Name: name,
|
||||
Type: plugin.PluginTypeProxy,
|
||||
Source: plugin.PluginSourceWASM,
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// loadMetadata 从 WASM 模块加载元数据
|
||||
func (p *WASMPlugin) loadMetadata(ctx context.Context) error {
|
||||
// 创建临时实例获取元数据
|
||||
inst, err := p.runtime.runtime.InstantiateModule(ctx, p.compiled, wazero.NewModuleConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer inst.Close(ctx)
|
||||
|
||||
metadataFn := inst.ExportedFunction("metadata")
|
||||
if metadataFn == nil {
|
||||
return fmt.Errorf("metadata function not exported")
|
||||
}
|
||||
|
||||
allocFn := inst.ExportedFunction("alloc")
|
||||
if allocFn == nil {
|
||||
return fmt.Errorf("alloc function not exported")
|
||||
}
|
||||
|
||||
// 分配缓冲区
|
||||
results, err := allocFn.Call(ctx, 1024)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bufPtr := uint32(results[0])
|
||||
|
||||
// 调用 metadata 函数
|
||||
results, err = metadataFn.Call(ctx, uint64(bufPtr), 1024)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
actualLen := uint32(results[0])
|
||||
|
||||
// 读取元数据
|
||||
mem := inst.Memory()
|
||||
data, ok := mem.Read(bufPtr, actualLen)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to read metadata")
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, &p.metadata)
|
||||
}
|
||||
|
||||
// Metadata 返回 plugin 信息
|
||||
func (p *WASMPlugin) Metadata() plugin.PluginMetadata {
|
||||
return p.metadata
|
||||
}
|
||||
|
||||
// Init 初始化 plugin
|
||||
func (p *WASMPlugin) Init(config map[string]string) error {
|
||||
p.config = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleConn 处理连接
|
||||
func (p *WASMPlugin) HandleConn(conn interface{}, dialer plugin.Dialer) error {
|
||||
// WASM plugin 的连接处理需要更复杂的实现
|
||||
// 这里提供基础框架,实际实现需要注册 host functions
|
||||
return fmt.Errorf("WASM plugin HandleConn not fully implemented")
|
||||
}
|
||||
|
||||
// Close 关闭 plugin
|
||||
func (p *WASMPlugin) Close() error {
|
||||
return p.compiled.Close(context.Background())
|
||||
}
|
||||
|
||||
// RegisterHostFunctions 注册 host functions 到 wazero 运行时
|
||||
func RegisterHostFunctions(ctx context.Context, r wazero.Runtime) (wazero.CompiledModule, error) {
|
||||
return r.NewHostModuleBuilder("env").
|
||||
NewFunctionBuilder().
|
||||
WithFunc(hostLog).
|
||||
Export("log").
|
||||
NewFunctionBuilder().
|
||||
WithFunc(hostNow).
|
||||
Export("now").
|
||||
Compile(ctx)
|
||||
}
|
||||
|
||||
// host function 实现
|
||||
func hostLog(ctx context.Context, m api.Module, level uint32, msgPtr, msgLen uint32) {
|
||||
data, ok := m.Memory().Read(msgPtr, msgLen)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
prefix := "[WASM]"
|
||||
switch plugin.LogLevel(level) {
|
||||
case plugin.LogDebug:
|
||||
prefix = "[WASM DEBUG]"
|
||||
case plugin.LogInfo:
|
||||
prefix = "[WASM INFO]"
|
||||
case plugin.LogWarn:
|
||||
prefix = "[WASM WARN]"
|
||||
case plugin.LogError:
|
||||
prefix = "[WASM ERROR]"
|
||||
}
|
||||
fmt.Printf("%s %s\n", prefix, string(data))
|
||||
}
|
||||
|
||||
func hostNow(ctx context.Context) int64 {
|
||||
return ctx.Value("now").(func() int64)()
|
||||
}
|
||||
116
pkg/plugin/wasm/runtime.go
Normal file
116
pkg/plugin/wasm/runtime.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package wasm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
// Runtime 管理 wazero WASM 运行时
|
||||
type Runtime struct {
|
||||
runtime wazero.Runtime
|
||||
modules map[string]*Module
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewRuntime 创建新的 WASM 运行时
|
||||
func NewRuntime(ctx context.Context) (*Runtime, error) {
|
||||
r := wazero.NewRuntime(ctx)
|
||||
return &Runtime{
|
||||
runtime: r,
|
||||
modules: make(map[string]*Module),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWazeroRuntime 返回底层 wazero 运行时
|
||||
func (r *Runtime) GetWazeroRuntime() wazero.Runtime {
|
||||
return r.runtime
|
||||
}
|
||||
|
||||
// LoadModule 从字节加载 WASM 模块
|
||||
func (r *Runtime) LoadModule(ctx context.Context, name string, wasmBytes []byte) (*Module, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if _, exists := r.modules[name]; exists {
|
||||
return nil, fmt.Errorf("module %s already loaded", name)
|
||||
}
|
||||
|
||||
compiled, err := r.runtime.CompileModule(ctx, wasmBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compile module: %w", err)
|
||||
}
|
||||
|
||||
module := &Module{
|
||||
name: name,
|
||||
compiled: compiled,
|
||||
}
|
||||
|
||||
r.modules[name] = module
|
||||
return module, nil
|
||||
}
|
||||
|
||||
// GetModule 获取已加载的模块
|
||||
func (r *Runtime) GetModule(name string) (*Module, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
m, ok := r.modules[name]
|
||||
return m, ok
|
||||
}
|
||||
|
||||
// UnloadModule 卸载 WASM 模块
|
||||
func (r *Runtime) UnloadModule(ctx context.Context, name string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
module, exists := r.modules[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("module %s not found", name)
|
||||
}
|
||||
|
||||
if err := module.Close(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(r.modules, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close 关闭运行时
|
||||
func (r *Runtime) Close(ctx context.Context) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
for name, module := range r.modules {
|
||||
if err := module.Close(ctx); err != nil {
|
||||
return fmt.Errorf("failed to close module %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return r.runtime.Close(ctx)
|
||||
}
|
||||
|
||||
// Module WASM 模块封装
|
||||
type Module struct {
|
||||
name string
|
||||
compiled wazero.CompiledModule
|
||||
instance api.Module
|
||||
}
|
||||
|
||||
// Name 返回模块名称
|
||||
func (m *Module) Name() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
// Close 关闭模块
|
||||
func (m *Module) Close(ctx context.Context) error {
|
||||
if m.instance != nil {
|
||||
if err := m.instance.Close(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return m.compiled.Close(ctx)
|
||||
}
|
||||
@@ -7,6 +7,12 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// 协议常量
|
||||
const (
|
||||
MaxMessageSize = 1024 * 1024 // 最大消息大小 1MB
|
||||
HeaderSize = 5 // 消息头大小
|
||||
)
|
||||
|
||||
// 消息类型定义
|
||||
const (
|
||||
MsgTypeAuth uint8 = 1 // 认证请求
|
||||
@@ -19,6 +25,15 @@ const (
|
||||
MsgTypeError uint8 = 8 // 错误消息
|
||||
MsgTypeProxyConnect uint8 = 9 // 代理连接请求 (SOCKS5/HTTP)
|
||||
MsgTypeProxyResult uint8 = 10 // 代理连接结果
|
||||
|
||||
// Plugin 相关消息
|
||||
MsgTypePluginList uint8 = 20 // 请求/响应可用 plugins
|
||||
MsgTypePluginDownload uint8 = 21 // 请求下载 plugin
|
||||
MsgTypePluginData uint8 = 22 // Plugin 二进制数据(分块)
|
||||
MsgTypePluginReady uint8 = 23 // Plugin 加载确认
|
||||
|
||||
// UDP 相关消息
|
||||
MsgTypeUDPData uint8 = 30 // UDP 数据包
|
||||
)
|
||||
|
||||
// Message 基础消息结构
|
||||
@@ -42,10 +57,14 @@ type AuthResponse struct {
|
||||
// ProxyRule 代理规则
|
||||
type ProxyRule struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type string `json:"type" yaml:"type"` // tcp, socks5, http
|
||||
LocalIP string `json:"local_ip" yaml:"local_ip"` // tcp 模式使用
|
||||
LocalPort int `json:"local_port" yaml:"local_port"` // tcp 模式使用
|
||||
Type string `json:"type" yaml:"type"` // 内置: tcp, udp, http, https; 插件: socks5 等
|
||||
LocalIP string `json:"local_ip" yaml:"local_ip"` // tcp/udp 模式使用
|
||||
LocalPort int `json:"local_port" yaml:"local_port"` // tcp/udp 模式使用
|
||||
RemotePort int `json:"remote_port" yaml:"remote_port"` // 服务端监听端口
|
||||
// Plugin 支持字段
|
||||
PluginName string `json:"plugin_name,omitempty" yaml:"plugin_name"`
|
||||
PluginVersion string `json:"plugin_version,omitempty" yaml:"plugin_version"`
|
||||
PluginConfig map[string]string `json:"plugin_config,omitempty" yaml:"plugin_config"`
|
||||
}
|
||||
|
||||
// ProxyConfig 代理配置下发
|
||||
@@ -75,9 +94,59 @@ type ProxyConnectResult struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// PluginMetadata Plugin 元数据(协议层)
|
||||
type PluginMetadata struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Checksum string `json:"checksum"`
|
||||
Size int64 `json:"size"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// PluginListRequest 请求可用 plugins
|
||||
type PluginListRequest struct {
|
||||
ClientVersion string `json:"client_version"`
|
||||
}
|
||||
|
||||
// PluginListResponse 返回可用 plugins
|
||||
type PluginListResponse struct {
|
||||
Plugins []PluginMetadata `json:"plugins"`
|
||||
}
|
||||
|
||||
// PluginDownloadRequest 请求下载 plugin
|
||||
type PluginDownloadRequest struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// PluginDataChunk Plugin 二进制数据块
|
||||
type PluginDataChunk struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
ChunkIndex int `json:"chunk_index"`
|
||||
TotalChunks int `json:"total_chunks"`
|
||||
Data []byte `json:"data"`
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
}
|
||||
|
||||
// PluginReadyNotification Plugin 加载确认
|
||||
type PluginReadyNotification struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// UDPPacket UDP 数据包
|
||||
type UDPPacket struct {
|
||||
RemotePort int `json:"remote_port"` // 服务端监听端口
|
||||
ClientAddr string `json:"client_addr"` // 客户端地址 (用于回复)
|
||||
Data []byte `json:"data"` // UDP 数据
|
||||
}
|
||||
|
||||
// WriteMessage 写入消息到 writer
|
||||
func WriteMessage(w io.Writer, msg *Message) error {
|
||||
header := make([]byte, 5)
|
||||
header := make([]byte, HeaderSize)
|
||||
header[0] = msg.Type
|
||||
binary.BigEndian.PutUint32(header[1:], uint32(len(msg.Payload)))
|
||||
|
||||
@@ -94,7 +163,7 @@ func WriteMessage(w io.Writer, msg *Message) error {
|
||||
|
||||
// ReadMessage 从 reader 读取消息
|
||||
func ReadMessage(r io.Reader) (*Message, error) {
|
||||
header := make([]byte, 5)
|
||||
header := make([]byte, HeaderSize)
|
||||
if _, err := io.ReadFull(r, header); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -102,7 +171,7 @@ func ReadMessage(r io.Reader) (*Message, error) {
|
||||
msgType := header[0]
|
||||
length := binary.BigEndian.Uint32(header[1:])
|
||||
|
||||
if length > 1024*1024 {
|
||||
if length > MaxMessageSize {
|
||||
return nil, errors.New("message too large")
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func (s *Server) HandleConn(conn net.Conn) {
|
||||
switch s.typ {
|
||||
case "socks5":
|
||||
err = s.socks5.HandleConn(conn)
|
||||
case "http":
|
||||
case "http", "https":
|
||||
err = s.http.HandleConn(conn)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
package relay
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const bufferSize = 32 * 1024
|
||||
|
||||
// Relay 双向数据转发
|
||||
func Relay(c1, c2 net.Conn) {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
|
||||
copy := func(dst, src net.Conn) {
|
||||
copyConn := func(dst, src net.Conn) {
|
||||
defer wg.Done()
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
n, err := src.Read(buf)
|
||||
if n > 0 {
|
||||
dst.Write(buf[:n])
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, bufferSize)
|
||||
_, _ = io.CopyBuffer(dst, src, buf)
|
||||
// 关闭写端,通知对方数据传输完成
|
||||
if tc, ok := dst.(*net.TCPConn); ok {
|
||||
tc.CloseWrite()
|
||||
}
|
||||
}
|
||||
|
||||
go copy(c1, c2)
|
||||
go copy(c2, c1)
|
||||
go copyConn(c1, c2)
|
||||
go copyConn(c2, c1)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user