apis

package
v0.4.2 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2026 License: GPL-2.0 Imports: 24 Imported by: 0

README

Sudoku API (Standard)

面向其他开发者开放的纯 Sudoku 协议 API:HTTP 伪装 + 数独 ASCII/Entropy 混淆 + AEAD 加密。支持带宽优化下行(enable_pure_downlink=false)与 UoT(UDP over TCP)。

安装

  • 推荐指定已有 tag:go get github.com/SUDOKU-ASCII/sudoku@v0.2.0
  • 或者直接跟随最新提交:go get github.com/SUDOKU-ASCII/sudoku

配置要点

  • 表格:sudoku.NewTable("your-seed", "prefer_ascii"|"prefer_entropy")sudoku.NewTableWithCustom("seed", "prefer_entropy", "xpxvvpvv")(2 个 x、2 个 p、4 个 v,ASCII 优先)。
  • 密钥:任意字符串即可,需两端一致,可用 ./sudoku -keygencrypto.GenerateMasterKey 生成。
  • AEAD:chacha20-poly1305(默认)或 aes-128-gcmnone 仅测试用。
  • 填充:PaddingMin/PaddingMax 为 0-100 的概率百分比。
  • 客户端:设置 ServerAddressTargetAddress
  • 服务端:可设置 HandshakeTimeoutSeconds 限制握手耗时。

客户端示例

package main

import (
	"context"
	"log"
	"time"

	"github.com/SUDOKU-ASCII/sudoku/apis"
	"github.com/SUDOKU-ASCII/sudoku/pkg/obfs/sudoku"
)

func main() {
	table, err := sudoku.NewTableWithCustom("seed-for-table", "prefer_entropy", "xpxvvpvv")
	if err != nil {
		log.Fatal(err)
	}

	cfg := &apis.ProtocolConfig{
		ServerAddress: "1.2.3.4:8443",
		TargetAddress: "example.com:443",
		Key:           "shared-key-hex-or-plain",
		AEADMethod:    "chacha20-poly1305",
		Table:         table,
		PaddingMin:    5,
		PaddingMax:    15,
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	conn, err := apis.Dial(ctx, cfg)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	// conn 即已完成握手的隧道,可直接读写应用层数据
}

服务端示例

package main

import (
	"io"
	"log"
	"net"

	"github.com/SUDOKU-ASCII/sudoku/apis"
	"github.com/SUDOKU-ASCII/sudoku/pkg/obfs/sudoku"
)

func main() {
	table, err := sudoku.NewTableWithCustom("seed-for-table", "prefer_entropy", "xpxvvpvv")
	if err != nil {
		log.Fatal(err)
	}
	// For rotation, build multiple tables and set cfg.Tables instead of cfg.Table.

	cfg := &apis.ProtocolConfig{
		Key:                     "shared-key-hex-or-plain",
		AEADMethod:              "chacha20-poly1305",
		Table:                   table,
		PaddingMin:              5,
		PaddingMax:              15,
		HandshakeTimeoutSeconds: 5,
	}

	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(err)
	}
	for {
		rawConn, err := ln.Accept()
		if err != nil {
			log.Println("accept:", err)
			continue
		}
		go func(c net.Conn) {
			defer c.Close()

			tunnel, target, userHash, err := apis.ServerHandshakeWithUserHash(c, cfg)
			if err != nil {
				// 握手失败时可按需 fallback;HandshakeError 携带已读数据
				log.Println("handshake:", err)
				return
			}
			defer tunnel.Close()

			_ = userHash // hex(sha256(privateKey)[:8]) for official split-key clients

			up, err := net.Dial("tcp", target)
			if err != nil {
				log.Println("dial target:", err)
				return
			}
			defer up.Close()

			go io.Copy(up, tunnel)
			io.Copy(tunnel, up)
		}(rawConn)
	}
}

CDN/代理模式(stream / poll / ws)

如需通过 CDN(例如 Cloudflare 小黄云)转发到服务端,设置 cfg.DisableHTTPMask=falsecfg.HTTPMaskMode="auto"(或 "stream" / "poll" / "ws"),并在 accept 后使用 apis.NewHTTPMaskTunnelServer(cfg).HandleConn

srv := apis.NewHTTPMaskTunnelServer(cfg)
for {
	rawConn, _ := ln.Accept()
	go func(c net.Conn) {
		defer c.Close()
		tunnel, target, handled, err := srv.HandleConn(c)
		if err != nil || !handled || tunnel == nil {
			return
		}
		defer tunnel.Close()
		_ = target
		io.Copy(tunnel, tunnel)
	}(rawConn)
}

HTTP 连接复用(HTTP/1.1 keep-alive / HTTP/2)

当开启 HTTP mask(stream / poll / auto / ws)时,设置 HTTPMaskMultiplex="auto" 会复用底层 HTTP 连接:

  • HTTP/1.1:keep-alive 复用连接池
  • HTTPS + HTTP/2:同一条 h2 连接可并发承载多条隧道(多路复用)

示例(每次 Dial 仍会做一次 Sudoku 握手,但可复用 TCP/TLS 连接):

base := &apis.ProtocolConfig{
	ServerAddress:      "your.domain.com:443",
	Key:                "shared-key-hex-or-plain",
	AEADMethod:         "chacha20-poly1305",
	Table:              table,
	PaddingMin:         5,
	PaddingMax:         15,
	EnablePureDownlink: true,
	DisableHTTPMask:    false,
	HTTPMaskMode:       "auto",
	HTTPMaskTLSEnabled: true,
	HTTPMaskMultiplex:  "auto",
}

cfg := *base
cfg.TargetAddress = "example.com:443"
c1, _ := apis.Dial(ctx, &cfg)
defer c1.Close()

单 tunnel 多目标(MuxClient)

当需要真正省掉后续“建 tunnel/握手”的 RTT(单条隧道内并发多目标连接),使用 apis.NewMuxClient

base := &apis.ProtocolConfig{
	ServerAddress:      "your.domain.com:443",
	Key:                "shared-key-hex-or-plain",
	AEADMethod:         "chacha20-poly1305",
	Table:              table,
	PaddingMin:         5,
	PaddingMax:         15,
	EnablePureDownlink: true,
	// HTTPMask/HTTP tunnel is recommended for CDN/proxy scenarios, but mux itself works on any tunnel.
	DisableHTTPMask: false,
	HTTPMaskMode:    "auto",
	HTTPMaskTLSEnabled: true,
}

mux, _ := apis.NewMuxClient(base)
defer mux.Close()

c1, _ := mux.Dial(ctx, "example.com:443")
defer c1.Close()

服务端自动识别(Forward / UoT / Mux / Reverse)

当服务端需要对齐 CLI 的全部会话类型(普通转发 / UoT / 单 tunnel 多目标 mux / 反向代理注册),可使用 ServerHandshakeSessionAutoWithUserHash

tunnelConn, session, target, userHash, err := apis.ServerHandshakeSessionAutoWithUserHash(rawConn, cfg)
if err != nil {
	return
}
switch session {
case apis.SessionForward:
	_ = target
case apis.SessionUoT:
	_ = apis.HandleUoT(tunnelConn)
case apis.SessionMux:
	_ = apis.HandleMuxServer(tunnelConn, nil)
case apis.SessionReverse:
	_ = userHash
}

反向代理(Reverse Proxy over Sudoku)

服务端创建一个 ReverseManager 作为 http.Handler,并在隧道连接上调用 HandleServerSession;客户端应优先使用 DialReverseClientSession,它会在 reverse 会话上自动强制 packed 上行,而普通代理流量仍保持 pure 上行:

mgr := apis.NewReverseManager()
_ = mgr // use as http.Handler

// Server side (after ServerHandshakeSessionAutoWithUserHash returns SessionReverse):
_ = mgr.HandleServerSession(tunnelConn, userHash)

// Client side:
_ = apis.DialReverseClientSession(ctx, cfg, "client-id", []apis.ReverseRoute{
	{Path: "/gitea", Target: "127.0.0.1:3000"},
})

兼容说明:

  • 旧写法 DialBase + ServeReverseClientSession 目前仍可工作,但它走的是 legacy pure 上行 reverse,仅用于兼容旧实现,后续会变为不兼容。

说明

  • DefaultConfig() 提供合理默认值,仍需设置 KeyTable 及对应的地址字段。
  • 服务端如需回落(HTTP/原始 TCP),可从 HandshakeError 取出 HTTPHeaderDataReadData 按顺序重放。
  • 带宽优化模式:将 enable_pure_downlink 设为 false 即可启用。
  • 如需 UoT,客户端调用 DialUDPOverTCP;服务端可用 ServerHandshakeAuto(或 HTTPMaskTunnelServer.HandleConnAuto)自动区分 TCP/UoT,随后对 UoT 连接调用 HandleUoT

Documentation

Overview

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Package apis exposes the Sudoku tunnel (HTTP mask + Sudoku obfuscation + AEAD) as a small Go API. It supports both pure Sudoku downlink and the bandwidth-optimized packed downlink, plus UDP-over-TCP (UoT), so the same primitives used by the CLI can be embedded by other projects.

Key entry points:

  • ProtocolConfig / DefaultConfig: describe all required parameters.
  • Dial: client-side helper that connects to a Sudoku server and sends the target address.
  • DialUDPOverTCP: client-side helper that primes a UoT tunnel.
  • ServerHandshake: server-side helper that upgrades an accepted TCP connection and returns the decrypted tunnel plus the requested target address (TCP mode).
  • ServerHandshakeFlexible: server-side helper that upgrades connections and lets callers detect UoT or read the target address themselves.
  • HandshakeError: wraps errors while preserving bytes already consumed so callers can gracefully fall back to raw TCP/HTTP handling if desired.

The configuration mirrors the CLI behavior: build a Sudoku table via sudoku.NewTable(seed, "prefer_ascii"|"prefer_entropy") or sudoku.NewTableWithCustom (third arg: custom X/P/V pattern such as "xpxvvpvv"), pick an AEAD (chacha20-poly1305 is the default), keep the key and padding settings consistent across client/server, and apply an optional handshake timeout on the server side.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Copyright (C) 2026 by saba <contact me via issue>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, no derivative work may use the name or imply association with this application without prior consent.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Dial

func Dial(ctx context.Context, cfg *ProtocolConfig) (net.Conn, error)

Dial opens a Sudoku tunnel to cfg.ServerAddress and requests cfg.TargetAddress.

func DialBase

func DialBase(ctx context.Context, cfg *ProtocolConfig) (net.Conn, error)

DialBase opens a Sudoku tunnel to cfg.ServerAddress and completes the handshake, but does not send a target address.

This is useful for higher-level protocols built on top of the tunnel (e.g. mux sessions, reverse proxy sessions).

func DialReverseClientSession

func DialReverseClientSession(ctx context.Context, cfg *ProtocolConfig, clientID string, routes []ReverseRoute) error

DialReverseClientSession dials the server and runs a reverse registration session until it ends. Reverse sessions use packed uplink by default; normal proxy sessions remain pure-uplink.

This function returns when the reverse session ends; callers usually want to reconnect with backoff.

func DialUDPOverTCP

func DialUDPOverTCP(ctx context.Context, cfg *ProtocolConfig) (net.Conn, error)

DialUDPOverTCP bootstraps a UDP-over-TCP tunnel using the standard Dial flow.

func HandleMuxServer

func HandleMuxServer(conn net.Conn, onConnect func(targetAddr string)) error

HandleMuxServer runs a "single tunnel, multi-target" mux session on an upgraded tunnel connection.

func HandleMuxWithDialer

func HandleMuxWithDialer(conn net.Conn, onConnect func(targetAddr string), dialTarget func(targetAddr string) (net.Conn, error)) error

HandleMuxWithDialer is like HandleMuxServer but allows the caller to control how targets are dialed.

func HandleUoT

func HandleUoT(conn net.Conn) error

HandleUoT runs the UDP-over-TCP loop on an upgraded tunnel connection.

func NewPreBufferedConn

func NewPreBufferedConn(conn net.Conn, preRead []byte) net.Conn

NewPreBufferedConn returns a net.Conn that replays preRead before reading from conn.

This is useful when you need to peek some bytes (e.g. to detect an HTTP tunnel header or probe tables) and still keep the stream consumable by the next parser.

func ReadTargetAddress

func ReadTargetAddress(r io.Reader) (string, error)

ReadTargetAddress parses a single SOCKS5-style target address frame (host:port) from r.

func ReadUoTDatagram

func ReadUoTDatagram(r io.Reader) (string, []byte, error)

func ServeReverseClientSession

func ServeReverseClientSession(conn net.Conn, clientID string, routes []ReverseRoute) error

ServeReverseClientSession registers routes to the server and serves reverse mux streams until the session ends.

The provided conn must be an upgraded Sudoku tunnel connection (handshake completed).

func ServerHandshake

func ServerHandshake(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, string, error)

ServerHandshake performs the Sudoku server-side handshake.

It completes the full handshake pipeline (HTTP mask → Sudoku decoding → AEAD decryption → timestamp verification) and then reads the target address from the tunnel.

On failure, the returned error may be a *HandshakeError containing raw data for fallback handling.

func ServerHandshakeAuto

func ServerHandshakeAuto(rawConn net.Conn, cfg *ProtocolConfig) (conn net.Conn, targetAddr string, isUoT bool, err error)

ServerHandshakeAuto upgrades the connection and detects whether it is a UoT (UDP-over-TCP) session.

Returns:

  • conn: the upgraded tunnel connection
  • targetAddr: valid only when isUoT=false
  • isUoT=true: caller should run HandleUoT(conn) instead of reading a target address

func ServerHandshakeAutoWithUserHash

func ServerHandshakeAutoWithUserHash(rawConn net.Conn, cfg *ProtocolConfig) (conn net.Conn, targetAddr string, isUoT bool, userHash string, err error)

ServerHandshakeAutoWithUserHash is like ServerHandshakeAuto but also returns the per-user handshake identifier.

func ServerHandshakeFlexible

func ServerHandshakeFlexible(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, func(error) error, error)

ServerHandshakeFlexible upgrades the connection and leaves payload parsing (address or UoT) to the caller. The returned fail function wraps errors into HandshakeError with recorded data for fallback handling.

func ServerHandshakeFlexibleWithUserHash

func ServerHandshakeFlexibleWithUserHash(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, string, func(error) error, error)

ServerHandshakeFlexibleWithUserHash is like ServerHandshakeFlexible but also returns the per-user handshake identifier.

func ServerHandshakeWithUserHash

func ServerHandshakeWithUserHash(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, string, string, error)

ServerHandshakeWithUserHash is like ServerHandshake but also returns a stable per-user identifier extracted from the client handshake: hex(sha256(privateKey)[:8]) for official clients using split private keys.

func WriteTargetAddress

func WriteTargetAddress(w io.Writer, addr string) error

WriteTargetAddress writes a single SOCKS5-style target address frame (host:port) to w.

func WriteUoTDatagram

func WriteUoTDatagram(w io.Writer, addr string, payload []byte) error

Types

type HTTPMaskTunnelServer

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

HTTPMaskTunnelServer bridges raw TCP accepts with the optional CDN-capable HTTP tunnel transports (stream/poll).

Typical usage:

srv := apis.NewHTTPMaskTunnelServer(serverCfg)
for {
  c, _ := ln.Accept()
  go func(raw net.Conn) {
    defer raw.Close()
    tunnel, target, handled, err := srv.HandleConn(raw)
    if err != nil || !handled || tunnel == nil {
      return
    }
    defer tunnel.Close()
    io.Copy(tunnel, tunnel)
  }(c)
}

func NewHTTPMaskTunnelServer

func NewHTTPMaskTunnelServer(cfg *ProtocolConfig) *HTTPMaskTunnelServer

func (*HTTPMaskTunnelServer) HandleConn

func (s *HTTPMaskTunnelServer) HandleConn(rawConn net.Conn) (tunnelConn net.Conn, targetAddr string, handled bool, err error)

HandleConn handles a single accepted TCP connection.

Returns:

  • tunnelConn/targetAddr if a Sudoku tunnel handshake has been completed (handled=true and tunnelConn != nil)
  • handled=true, tunnelConn=nil for HTTP tunnel control requests (e.g., poll push/pull)
  • handled=false if this server is not configured to handle HTTP tunnel transports (legacy-only)

func (*HTTPMaskTunnelServer) HandleConnAuto

func (s *HTTPMaskTunnelServer) HandleConnAuto(rawConn net.Conn) (tunnelConn net.Conn, targetAddr string, isUoT bool, handled bool, err error)

HandleConnAuto handles a single accepted TCP connection, and supports both TCP target connections and UoT (UDP-over-TCP) sessions.

Returns:

  • tunnelConn/targetAddr if a TCP target address has been read (handled=true, isUoT=false)
  • tunnelConn with isUoT=true if this is a UoT session (caller should run HandleUoT)
  • handled=true, tunnelConn=nil for HTTP tunnel control requests (e.g., poll push/pull)

func (*HTTPMaskTunnelServer) HandleConnAutoWithUserHash

func (s *HTTPMaskTunnelServer) HandleConnAutoWithUserHash(rawConn net.Conn) (tunnelConn net.Conn, targetAddr string, isUoT bool, userHash string, handled bool, err error)

HandleConnAutoWithUserHash is like HandleConnAuto but also returns the per-user handshake identifier when a Sudoku tunnel handshake has been completed.

func (*HTTPMaskTunnelServer) HandleConnWithUserHash

func (s *HTTPMaskTunnelServer) HandleConnWithUserHash(rawConn net.Conn) (tunnelConn net.Conn, targetAddr string, userHash string, handled bool, err error)

HandleConnWithUserHash is like HandleConn but also returns the per-user handshake identifier when a Sudoku tunnel handshake has been completed.

type HandshakeError

type HandshakeError struct {
	Err            error
	RawConn        net.Conn
	HTTPHeaderData []byte // Header data from the HTTP mask layer
	ReadData       []byte // Data already read by the Sudoku layer
}

HandshakeError wraps errors that occur during the handshake process. When this error is returned, the caller may perform fallback handling using RawConn, HTTPHeaderData, and ReadData.

Fields:

  • Err: Original error describing why the handshake failed.
  • RawConn: The raw TCP connection, available for fallback.
  • HTTPHeaderData: Header bytes read during the HTTP mask phase (ConsumeHeader stage).
  • ReadData: Bytes read and recorded during the Sudoku decoding phase.

Data replay order for fallback:

  1. Write HTTPHeaderData first (if non-empty)
  2. Then write ReadData (if non-empty)
  3. Finally forward remaining data from RawConn

Example usage:

conn, target, err := apis.ServerHandshake(rawConn, cfg)
if err != nil {
    var hsErr *apis.HandshakeError
    if errors.As(err, &hsErr) {
        // Perform fallback handling
        fallbackConn, _ := net.Dial("tcp", fallbackAddr)
        fallbackConn.Write(hsErr.HTTPHeaderData)
        fallbackConn.Write(hsErr.ReadData)
        io.Copy(fallbackConn, hsErr.RawConn)
        io.Copy(hsErr.RawConn, fallbackConn)
    }
    return
}

func (*HandshakeError) Error

func (e *HandshakeError) Error() string

func (*HandshakeError) Unwrap

func (e *HandshakeError) Unwrap() error

type HandshakeResult

type HandshakeResult struct {
	Conn     net.Conn          // Upgraded tunnel connection (decrypted / de-obfuscated).
	UserHash string            // Stable per-user identifier (hex, 16 chars). May be empty.
	Fail     func(error) error // Wraps an error into HandshakeError with recorded data for fallback.
}

HandshakeResult bundles all outputs from the server-side handshake into a single struct so that callers can pick the fields they need without multiple function variants.

func ServerHandshakeCore

func ServerHandshakeCore(rawConn net.Conn, cfg *ProtocolConfig) (*HandshakeResult, error)

ServerHandshakeCore performs the full server-side handshake and returns a HandshakeResult. It does NOT read the target address — the caller decides what to do with the tunnel.

type MuxClient

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

MuxClient opens multiple target connections over a single Sudoku tunnel (single tunnel, multi-target).

This avoids paying the HTTP tunnel + Sudoku handshake RTT for every target connection when HTTPMask tunnel modes are enabled.

func NewMuxClient

func NewMuxClient(cfg *ProtocolConfig) (*MuxClient, error)

func (*MuxClient) Close

func (c *MuxClient) Close() error

func (*MuxClient) Dial

func (c *MuxClient) Dial(ctx context.Context, targetAddr string) (net.Conn, error)

type ProtocolConfig

type ProtocolConfig struct {

	// ServerAddress is the server address (client-side only).
	// Format: "host:port" or "ip:port"
	// Example: "example.com:443" or "1.2.3.4:8080"
	ServerAddress string

	// Key is the pre-shared key used for AEAD encryption.
	// Both sides must agree on this value; use "./sudoku -keygen" to generate one.
	Key string

	// AEADMethod specifies the AEAD cipher.
	// Valid values:
	//   - "aes-128-gcm": AES-128-GCM (fast, good hardware acceleration)
	//   - "chacha20-poly1305": ChaCha20-Poly1305 (good pure-software performance)
	//   - "none": no encryption (testing only, never use in production)
	AEADMethod string

	// Table is the Sudoku encoding map (must be identical on client and server).
	// Create with sudoku.NewTable(seed, "prefer_ascii"|"prefer_entropy") or
	// sudoku.NewTableWithCustom(seed, "prefer_entropy", "<xpxvvpvv>").
	// Must not be nil (unless Tables is set).
	Table *sudoku.Table

	// Tables is an optional candidate set for table rotation.
	// If provided (len>0), the client will pick one table per connection and the server will
	// identify which one was used by probing the handshake uplink and, when needed, using the
	// KIP client-hello table hint for shared-uplink directional modes.
	// When Tables is set, Table may be nil.
	Tables []*sudoku.Table

	// PaddingMin is the minimum padding rate (0-100).
	// Minimum probability (%) of inserting a padding byte during encoding.
	PaddingMin int

	// PaddingMax is the maximum padding rate (0-100).
	// Maximum probability (%) of inserting a padding byte during encoding.
	// Must be >= PaddingMin.
	PaddingMax int

	// EnablePureDownlink controls downlink encoding.
	// When false, bandwidth-optimized 6-bit packed downlink is used.
	EnablePureDownlink bool

	// TargetAddress is the final destination the client wants to reach (client-side only).
	// Format: "host:port"
	// Example: "google.com:443" or "1.1.1.1:53"
	TargetAddress string

	// HandshakeTimeoutSeconds is the handshake timeout in seconds (server-side only).
	// Recommended: 5-10.
	// Too small may cause failures on slow networks.
	// Too large may make the server vulnerable to slowloris attacks.
	HandshakeTimeoutSeconds int

	// DisableHTTPMask disables the HTTP mask layer.
	// Default false (mask enabled).
	// When true, the client does not send a mask header and the server does not check for one.
	// Note: the server supports auto-detection, so even with this set to false it can handle
	// clients without a mask header (as long as the first bytes don't look like an HTTP method).
	DisableHTTPMask bool

	// HTTPMaskMode controls how the "HTTP mask" behaves:
	//   - "legacy": write a fake HTTP/1.1 header then switch to raw stream (default, not CDN-compatible)
	//   - "stream": real HTTP tunnel (split-stream), CDN-compatible
	//   - "poll": plain HTTP tunnel (authorize/push/pull), strong restricted-network pass-through
	//   - "auto": try stream then fall back to poll
	//   - "ws": WebSocket tunnel (ws:// or wss://)
	HTTPMaskMode string

	// HTTPMaskTLSEnabled enables HTTPS for HTTP tunnel modes (client-side).
	// When false, HTTP tunnel modes use plain HTTP (no port-based TLS inference).
	HTTPMaskTLSEnabled bool

	// HTTPMaskHost optionally overrides the HTTP Host header / SNI host for HTTP tunnel modes (client-side).
	// When empty, it is derived from ServerAddress.
	HTTPMaskHost string

	// HTTPMaskPathRoot optionally prefixes all HTTP mask paths with a first-level segment.
	// Example: "aabbcc" => "/aabbcc/session", "/aabbcc/api/v1/upload", ...
	HTTPMaskPathRoot string

	// HTTPMaskMultiplex controls multiplex behavior when HTTPMask tunnel modes are enabled:
	//   - "off": disable reuse; each Dial establishes its own HTTPMask tunnel
	//   - "auto": reuse underlying HTTP connections across multiple tunnel dials (HTTP/1.1 keep-alive / HTTP/2)
	//   - "on": enable "single tunnel, multi-target" mux when using apis.NewMuxClient (Dial behaves like "auto")
	HTTPMaskMultiplex string
}

ProtocolConfig defines all parameters required by the Sudoku protocol stack.

The Sudoku protocol is a multi-layer encrypted tunnel:

  1. HTTP mask layer: disguises traffic as HTTP POST requests
  2. Sudoku obfuscation layer: encodes data using Sudoku puzzle patterns
  3. AEAD encryption layer: provides confidentiality and integrity
  4. Protocol layer: handles handshake, address transfer, etc.

func DefaultConfig

func DefaultConfig() *ProtocolConfig

DefaultConfig returns a safe default configuration. Note: the returned config still requires Key, Table, ServerAddress (client) or TargetAddress (server).

func (*ProtocolConfig) Validate

func (c *ProtocolConfig) Validate() error

Validate checks the configuration for correctness. Returns the first error found, or nil if the configuration is valid.

func (*ProtocolConfig) ValidateClient

func (c *ProtocolConfig) ValidateClient() error

ValidateClient ensures the config carries the required client-side fields.

type ReverseManager

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

ReverseManager is a server-side reverse registry + http.Handler.

A reverse client registers routes over a Sudoku tunnel; this manager then exposes them via HTTP path prefixes.

func NewReverseManager

func NewReverseManager() *ReverseManager

func (*ReverseManager) HandleServerSession

func (m *ReverseManager) HandleServerSession(conn net.Conn, userHash string, helloPayload []byte) error

HandleServerSession handles a reverse client registration connection.

helloPayload is the JSON registration payload from the control plane.

func (*ReverseManager) ServeEntry

func (m *ReverseManager) ServeEntry(listenAddr string) error

ServeEntry listens on listenAddr and serves both HTTP reverse proxy requests (path-based routing) and raw TCP reverse forwarding (requires a route with Path=="") on the same port.

func (*ReverseManager) ServeHTTP

func (m *ReverseManager) ServeHTTP(w http.ResponseWriter, r *http.Request)

type ReverseRoute

type ReverseRoute struct {
	Path        string `json:"path"`
	Target      string `json:"target"`
	StripPrefix *bool  `json:"strip_prefix,omitempty"`
	HostHeader  string `json:"host_header,omitempty"`
}

ReverseRoute maps a public HTTP path prefix (or a raw TCP entry) to a client-side TCP target.

When Path is empty, the route becomes a raw TCP reverse mapping on the server entry (no HTTP). Only one TCP route is supported per entry.

type SessionKind

type SessionKind uint8

SessionKind describes the payload type carried by a Sudoku tunnel connection after the handshake.

The first control-plane message selects the session mode.

const (
	// SessionForward means the client will send a target address and then proxy TCP streams.
	SessionForward SessionKind = iota
	// SessionUoT means the tunnel carries UDP-over-TCP frames.
	SessionUoT
	// SessionMux means the tunnel carries a multiplexed stream session (single tunnel, multi-target).
	SessionMux
	// SessionReverse means the tunnel is used to register reverse proxy routes (server exposes client services).
	SessionReverse
)

func ServerHandshakeSessionAuto

func ServerHandshakeSessionAuto(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, SessionKind, string, error)

ServerHandshakeSessionAuto is like ServerHandshakeSessionAutoWithUserHash but omits the user hash.

func ServerHandshakeSessionAutoWithUserHash

func ServerHandshakeSessionAutoWithUserHash(rawConn net.Conn, cfg *ProtocolConfig) (conn net.Conn, session SessionKind, targetAddr string, userHash string, sessionPayload []byte, err error)

ServerHandshakeSessionAutoWithUserHash upgrades the connection and auto-detects the session kind.

Returns:

  • conn: the upgraded tunnel connection. The control-plane message is always consumed.
  • session: the detected session kind
  • targetAddr: valid only when session==SessionForward
  • userHash: per-user handshake identifier (if available)
  • sessionPayload: valid only when session==SessionReverse (the JSON registration payload)

Jump to

Keyboard shortcuts

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