tunnel

package
v0.0.0-...-399ff89 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package tunnel 提供基于 AES-256-GCM 的加密隧道转发功能。

核心能力

  • 密钥管理:生成随机密钥(GenerateKey)、解析十六进制密钥(ParseKey)
  • 加解密:AES-256-GCM 加密(Encrypt)和解密(Decrypt),nonce 随机生成并前置
  • 编解码:Base64 编码(EncodeBody)和解码(DecodeBody),用于传输二进制数据
  • 服务端:NewHandler / NewLocalHandler 返回标准 http.Handler,可嵌入任意 HTTP 服务
  • 本地路由:NewLocalHandler 支持将相对路径请求直接路由到本地 handler,无需外部 HTTP 调用
  • 客户端:Client 结构体提供 Do 方法,发送加密请求并解密响应
  • 密钥轮换:Handler.UpdateKey 支持在运行时热替换密钥,旧密钥保留短时窗口供存量连接使用
  • 多路复用隧道:NewTunnel 基于 mux 层创建持久双向隧道,支持流复用和双向 HTTP 请求交换

协议格式

所有请求/响应统一使用帧协议(application/x-tunnel-frame):

[4B big-endian metaLen][encrypted metadata][stream chunks...]

其中 encrypted metadata = Encrypt(key, metaJSON, AADMeta),stream chunks 由 EncryptStream(key, body, writer, AADStream) 生成, 格式为 [2B chunkLen][nonce|ciphertext|tag],每块独立加密。 body 为空时 stream chunks 部分为零字节。

安全特性

  • 每次加密使用随机 nonce(12 字节),相同明文产生不同密文
  • GCM 模式提供认证加密,可检测篡改
  • 密钥为 32 字节(AES-256),需通过安全通道分发
  • UpdateKey 热替换密钥时,旧密钥仍可解密存量连接(短时窗口)

使用示例

服务端嵌入:

key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
mux := http.NewServeMux()
mux.Handle("POST /tunnel", tunnel.NewHandler(key))
http.ListenAndServe(":8080", mux)

客户端调用(标准库风格):

client, _ := tunnel.NewClient("0123456789abcdef...", "http://proxy:8080/tunnel", 30*time.Second)
req, _ := http.NewRequest("GET", "https://api.example.com/data", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
Example
package main

import (
	"fmt"
	"os"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	os.Setenv("TUNNEL_KEY", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
	defer os.Unsetenv("TUNNEL_KEY")

	hexKey := os.Getenv("TUNNEL_KEY")

	key, err := tunnel.ParseKey(hexKey)
	if err != nil {
		fmt.Println("parse key error:", err)
		return
	}

	plaintext, err := tunnel.Encrypt(key, []byte("hello"), []byte(tunnel.AADMeta))
	if err != nil {
		return
	}

	decrypted, err := tunnel.Decrypt(key, plaintext, []byte(tunnel.AADMeta))
	if err != nil {
		return
	}

	fmt.Println(string(decrypted))
}
Output:
hello
Example (EncryptDecrypt)
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
	plaintext := []byte("hello secure tunnel")

	encrypted, err := tunnel.Encrypt(key, plaintext, []byte(tunnel.AADMeta))
	if err != nil {
		fmt.Println("encrypt error:", err)
		return
	}

	decrypted, err := tunnel.Decrypt(key, encrypted, []byte(tunnel.AADMeta))
	if err != nil {
		fmt.Println("decrypt error:", err)
		return
	}

	fmt.Println(string(decrypted))
}
Output:
hello secure tunnel
Example (EncryptStreamDecryptStream)
package main

import (
	"bytes"
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	original := []byte("hello streaming world with chunked AES-256-GCM encryption")
	var encrypted bytes.Buffer

	n, err := tunnel.EncryptStream(key, bytes.NewReader(original), &encrypted, []byte(tunnel.AADStream))
	if err != nil {
		fmt.Println("encrypt error:", err)
		return
	}
	_ = n

	var decrypted bytes.Buffer
	n2, err := tunnel.DecryptStream(key, bytes.NewReader(encrypted.Bytes()), &decrypted, []byte(tunnel.AADStream))
	if err != nil {
		fmt.Println("decrypt error:", err)
		return
	}
	_ = n2

	fmt.Println(decrypted.String())
}
Output:
hello streaming world with chunked AES-256-GCM encryption
Example (NewHandlerEmptyKey)
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(nil, nil))

	server := httptest.NewServer(mux)
	defer server.Close()

	resp, err := http.Post(server.URL+"/tunnel", "application/octet-stream", nil)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()
	fmt.Println(resp.StatusCode)
}
Output:
403
Example (RandomNonce)
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
	plaintext := []byte("same plaintext")

	c1, _ := tunnel.Encrypt(key, plaintext, []byte(tunnel.AADMeta))
	c2, _ := tunnel.Encrypt(key, plaintext, []byte(tunnel.AADMeta))

	fmt.Println(string(c1) != string(c2))
}
Output:
true
Example (TamperDetection)
package main

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(key, nil))
	server := httptest.NewServer(mux)
	defer server.Close()

	// 发送损坏的帧(非法 metadata),服务端应返回 400
	resp, err := http.Post(server.URL+"/tunnel", "application/x-tunnel-frame", bytes.NewReader([]byte("corrupted frame data")))
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()
	fmt.Println(resp.StatusCode)
}
Output:
400

Index

Examples

Constants

View Source
const (

	// MaxMetadataBytes 限制 metadata 帧的最大长度(1 MiB),防止远程攻击者通过伪造的 metaLen 触发 OOM。
	// 合法 metadata 通常仅几百字节到几 KiB,1 MiB 已留足上限。
	MaxMetadataBytes = 1 << 20
	// AADMeta 是 metadata 加密使用的 AAD(Additional Authenticated Data)上下文标签。
	// 将密文绑定到"隧道 metadata 帧"这一特定上下文,防止密文被重放到其他上下文(如 stream body)。
	AADMeta = "tunnel:meta:v1"
	// AADStream 是流式 body 加密使用的 AAD 上下文标签。
	// 将密文绑定到"隧道流式 body"这一特定上下文,防止密文被重放到其他上下文(如 metadata 帧)。
	AADStream = "tunnel:stream:v1"
)

Variables

View Source
var DefaultChunkSize = 64 * 1024

DefaultChunkSize 是 EncryptStream 默认的块大小(64 KB)。 每个块使用随机 nonce 独立加密。可在调用 EncryptStream 前修改此变量以调整块大小。

View Source
var ErrMetadataTooLarge = fmt.Errorf("metadata frame too large (> %d bytes)", MaxMetadataBytes)

ErrMetadataTooLarge 表示 metadata 帧长度超过 MaxMetadataBytes。

Functions

func Decrypt

func Decrypt(key, data, aad []byte) ([]byte, error)

Decrypt 使用 AES-256-GCM 解密由 Encrypt 生成的密文。

自动从密文中提取 nonce(前 12 字节),然后进行 GCM 解密和认证。 如果密文被篡改或密钥不匹配,返回错误。 aad 必须与加密时使用的 aad 一致,否则解密失败。

func DecryptStream

func DecryptStream(key []byte, r io.Reader, w io.Writer, aad []byte) (int64, error)

DecryptStream 从 r 中读取 EncryptStream 生成的帧格式密文, 使用 AES-256-GCM 解密每个块,并将明文写入 w。

读取格式:先读 4 字节大端序长度获取块密文长度,再读取对应字节数的密文块。 每个密文块格式为 [nonce(12字节) | ciphertext | tag(16字节)], nonce 为块的前 12 字节,用于 GCM 解密。

aad 必须与加密时使用的 aad 一致,否则解密失败。 返回写入 w 的总字节数。当 r 返回 io.EOF(无更多块)时正常结束。 如果任一块解密失败,返回错误。

DecryptStream 等价于 DecryptStreamWithChunkSize(key, r, w, maxChunkLen, aad)。

func DecryptStreamWithChunkSize

func DecryptStreamWithChunkSize(key []byte, r io.Reader, w io.Writer, maxChunkSize int, aad []byte) (int64, error)

DecryptStreamWithChunkSize 与 DecryptStream 行为一致,但允许指定最大块大小 maxChunkSize。

maxChunkSize 为单帧密文最大允许字节数,超出时返回错误,防止恶意超大帧触发 OOM。

func Encrypt

func Encrypt(key, plaintext, aad []byte) ([]byte, error)

Encrypt 使用 AES-256-GCM 加密明文,并通过 aad 将密文绑定到特定上下文。

返回的密文格式为:nonce(12 字节) + ciphertext + auth_tag(16 字节)。 每次调用生成随机 nonce,相同明文产生不同密文。 aad 是额外认证数据(Additional Authenticated Data),不需要保密,但任何修改都会导致解密失败。 用于防止密文混淆攻击:将密文绑定到特定上下文(如协议版本、流方向)。

func EncryptStream

func EncryptStream(key []byte, r io.Reader, w io.Writer, aad []byte) (int64, error)

EncryptStream 从 r 中分块读取数据,使用 AES-256-GCM 独立加密每个块, 并将帧格式的密文写入 w。

帧格式:每个块先写入 4 字节大端序的密文长度,再写入 [nonce(12字节) | ciphertext | tag(16字节)]。 每个块使用随机 nonce 独立加密,块大小为 DefaultChunkSize(64KB), 最后一个块可能小于该值。

aad 是额外认证数据,将每个块密文绑定到特定上下文(如 AADStream)。 返回写入 w 的总字节数。当 r 返回 io.EOF 时正常结束。

EncryptStream 等价于 EncryptStreamWithChunkSize(key, r, w, DefaultChunkSize, aad)。

func EncryptStreamWithChunkSize

func EncryptStreamWithChunkSize(key []byte, r io.Reader, w io.Writer, chunkSize int, aad []byte) (int64, error)

EncryptStreamWithChunkSize 与 EncryptStream 行为一致,但允许指定块大小 chunkSize。

chunkSize 每块的明文大小。较大的块可降低帧头(4 字节长度 + nonce)开销, 但会增大单次分配和加密延迟。

func GenerateKey

func GenerateKey() (string, error)

GenerateKey 使用 crypto/rand 生成一个随机的 AES-256 密钥,返回 64 字符的十六进制字符串。

生成的密钥可用于配置 sproxy 的 tunnel_key 和 sclient 的 tunnel_key。

Example
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, err := tunnel.GenerateKey()
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Printf("len=%d\n", len(key))
}
Output:
len=64

func NewHandler

func NewHandler(key []byte, logger *slog.Logger) http.Handler

NewHandler 创建一个仅支持外部转发的加密隧道处理器。

行为同旧版闭包实现。如果 key 为空,处理器直接返回 403 Forbidden。 logger 为 nil 时使用 slog.Default()。 使用方式:mux.Handle("POST /tunnel", tunnel.NewHandler(key))

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("target response"))
	}))
	defer targetServer.Close()

	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(key, nil))
	proxyServer := httptest.NewServer(mux)
	defer proxyServer.Close()

	client, _ := tunnel.NewClient(
		"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
		proxyServer.URL+"/tunnel",
		5*time.Second,
		nil,
	)

	req, _ := http.NewRequest("GET", targetServer.URL, nil)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println(resp.Status)
}
Output:
200 OK

func NewLocalHandler

func NewLocalHandler(key []byte, local http.Handler, logger *slog.Logger) http.Handler

NewLocalHandler 创建一个支持本地路由和外部转发的加密隧道处理器。

当请求 URL 为绝对路径(如 /upload)且在 local 中注册时,直接在当前进程中转发到 local handler; 当请求 URL 为绝对 URL(如 https://example.com/api)时,与原 NewHandler 行为一致。 logger 为 nil 时使用 slog.Default()。

func ParseKey

func ParseKey(hexKey string) ([]byte, error)

ParseKey 将十六进制字符串解析为 32 字节的 AES-256 密钥。

hexKey 必须是 64 个十六进制字符(编码 32 字节)。 返回的密钥可直接用于 Encrypt、Decrypt、NewHandler、NewClient。

Example
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, err := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Printf("len=%d\n", len(key))
}
Output:
len=32
Example (InvalidLength)
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	_, err := tunnel.ParseKey("00ff")
	fmt.Println(err)
}
Output:
key must be 32 bytes (64 hex chars)

Types

type Client

type Client struct {
	Key        []byte
	TunnelURL  string
	HTTPClient *http.Client
	// contains filtered or unexported fields
}

Client 是加密隧道客户端,用于向隧道服务端发送加密请求并接收解密响应。

零值不可用,必须通过 NewClient 创建。

func NewClient

func NewClient(hexKey, tunnelURL string, timeout time.Duration, logger *slog.Logger) (*Client, error)

NewClient 创建一个加密隧道客户端。

参数:

  • hexKey: 64 位十六进制密钥字符串,与 sproxy 服务端 tunnel_key 一致
  • tunnelURL: 隧道服务端地址,如 "http://proxy:8080/tunnel"
  • timeout: HTTP 客户端超时时间
  • logger: 日志记录器,为 nil 时使用 slog.Default()

如果 hexKey 格式无效(非 64 位十六进制),返回错误。

func (*Client) Do

func (c *Client) Do(req *http.Request) (*http.Response, error)

Do 接受标准 *http.Request,通过加密隧道转发并返回标准 *http.Response。

使用标准库类型,调用方零学习成本。 所有请求/响应统一使用流式帧协议,内存占用恒定(不超过单个加密块大小)。 返回的 *http.Response.Body 为流式 Reader,调用方可边读边消费,关闭时自动释放底层连接。 目标返回非 2xx 状态码时,仍返回 *http.Response(非 error),StatusCode 正确反映目标状态。

Example
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Response-Header", "test-value")
		w.WriteHeader(http.StatusOK)
		fmt.Fprintf(w, `{"method":"%s","path":"%s"}`, r.Method, r.URL.Path)
	}))
	defer targetServer.Close()

	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(key, nil))
	proxyServer := httptest.NewServer(mux)
	defer proxyServer.Close()

	client, _ := tunnel.NewClient(
		"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
		proxyServer.URL+"/tunnel",
		5*time.Second,
		nil,
	)

	req, _ := http.NewRequest("GET", targetServer.URL+"/api/hello", nil)
	req.Header.Set("X-Custom", "test")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()

	fmt.Println(resp.StatusCode)
	fmt.Println(resp.Header.Get("X-Response-Header"))

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
Output:
200
test-value
{"method":"GET","path":"/api/hello"}
Example (LargeBody)
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := io.ReadAll(r.Body)
		fmt.Fprintf(w, `{"size":%d}`, len(body))
	}))
	defer targetServer.Close()

	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(key, nil))
	proxyServer := httptest.NewServer(mux)
	defer proxyServer.Close()

	client, _ := tunnel.NewClient(
		"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
		proxyServer.URL+"/tunnel",
		10*time.Second,
		nil,
	)

	// 使用 ≥ 64KB 的数据验证流式传输(2 个完整 chunk)
	largeBody := make([]byte, 128*1024)
	for i := range largeBody {
		largeBody[i] = byte('A' + (i % 26))
	}

	req, _ := http.NewRequest("POST", targetServer.URL, bytes.NewReader(largeBody))
	req.Header.Set("Content-Type", "application/octet-stream")

	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()

	fmt.Println(resp.StatusCode)

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
}
Output:
200
{"size":131072}
Example (StreamResponse)
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"time"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	key, _ := tunnel.ParseKey("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

	const responseSize = 128 * 1024 // 128KB

	targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		data := make([]byte, responseSize)
		for i := range data {
			data[i] = byte('Z' - (i % 26))
		}
		w.WriteHeader(http.StatusOK)
		w.Write(data)
	}))
	defer targetServer.Close()

	mux := http.NewServeMux()
	mux.Handle("POST /tunnel", tunnel.NewHandler(key, nil))
	proxyServer := httptest.NewServer(mux)
	defer proxyServer.Close()

	client, _ := tunnel.NewClient(
		"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
		proxyServer.URL+"/tunnel",
		10*time.Second,
		nil,
	)

	req, _ := http.NewRequest("GET", targetServer.URL, nil)
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()

	// 流式消费:io.Copy 边解密边读,内存占用恒定
	var buf bytes.Buffer
	n, err := io.Copy(&buf, resp.Body)
	if err != nil {
		fmt.Println("copy error:", err)
		return
	}

	fmt.Println(resp.StatusCode)
	fmt.Println(n == responseSize)
}
Output:
200
true

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler 处理加密隧道请求,支持外部转发和本地路由两种模式。

外部转发(默认):将加密请求解密后转发到外部目标 URL。 本地路由:当配置了 localHandler 且请求 URL 为相对路径时,将请求直接路由到本地 handler。

两种模式统一使用流式帧协议,响应体通过 Pipe 流式加密,不缓冲在内存中。

密钥轮换:通过 UpdateKey 可运行时热替换密钥,旧密钥保留短时窗口供存量连接完成。 所有新加密使用新密钥;解密时先尝试新密钥,不匹配则尝试旧密钥。 重放保护:ServiceHTTP 中解析 metadata 后调用 replayProtector.Validate 检测重放攻击。

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Handler) UpdateKey

func (h *Handler) UpdateKey(newKey []byte)

UpdateKey 热替换隧道加密密钥。

调用后,新连接使用 newKey 加密;存量连接仍可用旧密钥解密(写入者已使用旧密钥加密的流)。 多次调用只保留最近两代密钥(当前 + 前一代),更早的密钥不再接受。

type ReplayProtector

type ReplayProtector struct {
	// contains filtered or unexported fields
}

ReplayProtector 用于检测和拒绝重放的隧道帧。

func NewReplayProtector

func NewReplayProtector() *ReplayProtector

NewReplayProtector 创建重放保护器。

func (*ReplayProtector) Len

func (rp *ReplayProtector) Len() int

Len 返回当前保护的条目数(用于测试)。

func (*ReplayProtector) Validate

func (rp *ReplayProtector) Validate(jti string, iat int64) error

Validate 验证 jti 和 iat 是否有效。 返回 nil 表示通过验证,拒绝重放。

type Request

type Request struct {
	Method  string            `json:"method"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers"`
	IAT     int64             `json:"iat,omitempty"` // 签发时间(Unix 秒),用于重放保护
	JTI     string            `json:"jti,omitempty"` // 请求唯一 ID(hex 16 字节),用于重放保护
}

Request 表示一个加密隧道请求,包含要转发到目标服务器的 HTTP 请求信息。

Example
package main

import (
	"fmt"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	req := &tunnel.Request{
		Method:  "POST",
		URL:     "https://api.example.com/echo",
		Headers: map[string]string{"Content-Type": "application/json"},
	}

	fmt.Println(req.Method)
	fmt.Println(req.URL)
	fmt.Println(req.Headers["Content-Type"])

}
Output:
POST
https://api.example.com/echo
application/json

type Response

type Response struct {
	Proto         string      `json:"proto"`
	Status        int         `json:"status"`
	Headers       http.Header `json:"headers"`
	ContentLength int64       `json:"content_length"`
}

Response 表示加密隧道响应,包含目标服务器返回的 HTTP 响应信息。

Example
package main

import (
	"fmt"
	"net/http"

	"github.com/cocomhub/sproxy/pkg/tunnel"
)

func main() {
	resp := tunnel.Response{
		Status: 200,
		Headers: http.Header{
			"Content-Type": {"application/json"},
		},
	}

	fmt.Println(resp.Status)
	fmt.Println(resp.Headers.Get("Content-Type"))

}
Output:
200
application/json

type StreamDecryptor

type StreamDecryptor struct {
	// contains filtered or unexported fields
}

StreamDecryptor 封装 AES-256-GCM 流式解密。

func NewStreamDecryptor

func NewStreamDecryptor(key []byte, maxChunkLen int) (*StreamDecryptor, error)

NewStreamDecryptor 创建流解密器。

func (*StreamDecryptor) DecryptChunk

func (d *StreamDecryptor) DecryptChunk(r io.Reader, w io.Writer, aad []byte) (int, error)

DecryptChunk 从 r 中读取一个加密块并使用 aad 解密,将明文写入 w。 aad 必须与加密时使用的 aad 一致,否则解密失败。

func (*StreamDecryptor) DecryptStream

func (d *StreamDecryptor) DecryptStream(r io.Reader, w io.Writer, aad []byte) (int64, error)

DecryptStream 从 r 中读取并解密流,将明文写入 w。 每个块使用 aad 作为 GCM 的额外认证数据。

type StreamEncryptor

type StreamEncryptor struct {
	// contains filtered or unexported fields
}

StreamEncryptor 封装 AES-256-GCM 流式加密。

func NewStreamEncryptor

func NewStreamEncryptor(key []byte, chunkSize int) (*StreamEncryptor, error)

NewStreamEncryptor 创建流加密器。

func (*StreamEncryptor) EncryptChunk

func (e *StreamEncryptor) EncryptChunk(plaintext []byte, w io.Writer, aad []byte) (int, error)

EncryptChunk 加密 single plaintext 块并写入 w。 使用 aad 作为 GCM 的额外认证数据,将密文绑定到特定上下文。 返回写入的字节数(4B 长度前缀 + nonce + ciphertext + tag)。

func (*StreamEncryptor) EncryptStream

func (e *StreamEncryptor) EncryptStream(r io.Reader, w io.Writer, aad []byte) (int64, error)

EncryptStream 使用流加密器从 r 中分块读取数据并加密写入 w。 每个块使用 aad 作为 GCM 的额外认证数据。 返回写入 w 的总字节数。

type Tunnel

type Tunnel struct {
	// contains filtered or unexported fields
}

Tunnel 在一条 mux 多路复用连接之上提供 HTTP 请求-响应交换。

func NewTunnel

func NewTunnel(m *mux.Mux, key []byte) *Tunnel

func (*Tunnel) Do

func (t *Tunnel) Do(req *http.Request) (*http.Response, error)

Do 发送 HTTP 请求并返回响应。

func (*Tunnel) Serve

func (t *Tunnel) Serve(ctx context.Context, handler http.Handler) error

Serve 在隧道上提供 HTTP 服务。 在进入 accept 循环前,同步执行 ECDH 握手(listener 侧),超时后回退到静态密钥。

Directories

Path Synopsis
Package hub 提供中继网络的核心类型与接口。
Package hub 提供中继网络的核心类型与接口。
Package p2p provides a peer-to-peer transport layer that combines DHT discovery with WebRTC transport and stream multiplexing into a complete P2P link.
Package p2p provides a peer-to-peer transport layer that combines DHT discovery with WebRTC transport and stream multiplexing into a complete P2P link.
Package tracing provides a lightweight OpenTelemetry-like tracing skeleton built on standard log/slog, with no external dependencies.
Package tracing provides a lightweight OpenTelemetry-like tracing skeleton built on standard log/slog, with no external dependencies.
Package xfer 定义传输层抽象接口和注册表。
Package xfer 定义传输层抽象接口和注册表。
internal/tcp
Package tcp 提供基于 TCP 的 xfer.Conn 传输层实现。
Package tcp 提供基于 TCP 的 xfer.Conn 传输层实现。
xfertest
Package xfertest 提供 xfer.Conn 的测试工具。
Package xfertest 提供 xfer.Conn 的测试工具。
ext/webrtc module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL