init
This commit is contained in:
commit
874d625f2d
BIN
4to6-linux-amd64
Normal file
BIN
4to6-linux-amd64
Normal file
Binary file not shown.
BIN
4to6-linux-arm64
Normal file
BIN
4to6-linux-arm64
Normal file
Binary file not shown.
9
4to6.conf
Normal file
9
4to6.conf
Normal file
@ -0,0 +1,9 @@
|
||||
global {
|
||||
// 配置多个端口转发规则, 键是监听地址(IPv4), 值为目标地址(可以是IPv6).
|
||||
|
||||
// TCP 转发规则
|
||||
LT = 0.0.0.0:2222 -> [2408:8221:9901:8895:4c32:7173:b476:b65c]:22
|
||||
|
||||
// UDP 转发规则
|
||||
LU = 0.0.0.0:53 -> v6.aixiao.me:53
|
||||
}
|
8
build.sh
Normal file
8
build.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
BIN=4to6
|
||||
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BIN}-linux-amd64 -a -ldflags '-extldflags "-static"'
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ${BIN}-linux-arm64 -a -ldflags '-extldflags "-static"'
|
139
config.go
Normal file
139
config.go
Normal file
@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config 结构体存储配置信息
|
||||
type Config struct {
|
||||
Global struct {
|
||||
LT map[string]string // 存储本地地址到目标地址的映射
|
||||
LU map[string]string // 存储本地地址到目标地址的映射
|
||||
}
|
||||
}
|
||||
|
||||
// isValidHostname 验证主机名格式
|
||||
func isValidHostname(hostname string) bool {
|
||||
if len(hostname) > 255 || hostname == "" {
|
||||
return false
|
||||
}
|
||||
for _, part := range strings.Split(hostname, ".") {
|
||||
if len(part) == 0 || len(part) > 63 {
|
||||
return false
|
||||
}
|
||||
for _, c := range part {
|
||||
if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parseAddress 解析并验证地址字符串
|
||||
func parseAddress(address string) (string, int, error) {
|
||||
host, portStr, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("地址格式错误: %s (格式应为 host:port)", address)
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
return "", 0, fmt.Errorf("无效的端口号: %s", address)
|
||||
}
|
||||
|
||||
host = strings.Trim(host, "[]") // 去除 IPv6 方括号
|
||||
if net.ParseIP(host) == nil && !isValidHostname(host) {
|
||||
return "", 0, fmt.Errorf("无效的主机名或 IP: %s", address)
|
||||
}
|
||||
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
// parseConfig 解析配置文件
|
||||
func parseConfig(filename string) (Config, error) {
|
||||
var config Config
|
||||
config.Global.LT = make(map[string]string)
|
||||
config.Global.LU = make(map[string]string)
|
||||
var currentSection string
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(line, "{") {
|
||||
currentSection = strings.Fields(line)[0]
|
||||
continue
|
||||
}
|
||||
if line == "}" {
|
||||
currentSection = ""
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(strings.TrimSuffix(parts[1], ";"))
|
||||
|
||||
if currentSection == "global" && key == "LT" {
|
||||
lsParts := strings.SplitN(value, "->", 2)
|
||||
if len(lsParts) == 2 {
|
||||
localAddr := strings.TrimSpace(lsParts[0])
|
||||
targetAddr := strings.TrimSpace(lsParts[1])
|
||||
|
||||
if _, _, err := parseAddress(localAddr); err != nil {
|
||||
return config, fmt.Errorf("本地地址格式错误: %s", localAddr)
|
||||
}
|
||||
if _, _, err := parseAddress(targetAddr); err != nil {
|
||||
return config, fmt.Errorf("目标地址格式错误: %s", targetAddr)
|
||||
}
|
||||
|
||||
config.Global.LT[localAddr] = targetAddr
|
||||
} else {
|
||||
return config, fmt.Errorf("LT 配置格式错误: %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
if currentSection == "global" && key == "LU" {
|
||||
lsParts := strings.SplitN(value, "->", 2)
|
||||
if len(lsParts) == 2 {
|
||||
localAddr := strings.TrimSpace(lsParts[0])
|
||||
targetAddr := strings.TrimSpace(lsParts[1])
|
||||
|
||||
if _, _, err := parseAddress(localAddr); err != nil {
|
||||
return config, fmt.Errorf("本地地址格式错误: %s", localAddr)
|
||||
}
|
||||
if _, _, err := parseAddress(targetAddr); err != nil {
|
||||
return config, fmt.Errorf("目标地址格式错误: %s", targetAddr)
|
||||
}
|
||||
|
||||
config.Global.LU[localAddr] = targetAddr
|
||||
} else {
|
||||
return config, fmt.Errorf("LU 配置格式错误: %s", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return config, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
51
daemon.go
Normal file
51
daemon.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// Daemon 函数用于将程序转换为守护进程模式(简易实现)
|
||||
// 注意:这不是 POSIX 标准的完整守护进程实现,缺少以下标准步骤:
|
||||
// 1. 未创建新会话(setsid)
|
||||
// 2. 未重置文件权限掩码
|
||||
// 3. 未改变工作目录
|
||||
// 4. 未关闭/重定向标准输入输出
|
||||
// 适用于简单后台需求,生产环境建议使用专业库
|
||||
func Daemon() {
|
||||
// 构建新的命令行参数(过滤掉 -d 参数)
|
||||
args := make([]string, 0)
|
||||
for _, arg := range os.Args[1:] { // 遍历原始参数(跳过第一个程序路径参数)
|
||||
if arg != "-d" { // 过滤掉用于触发守护进程的 -d 标志
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新的进程命令
|
||||
// os.Args[0] 是当前程序的执行路径
|
||||
// args 是过滤后的新参数列表(不包含 -d)
|
||||
cmd := exec.Command(os.Args[0], args...)
|
||||
|
||||
// 配置标准输入输出(注意:真正的守护进程通常需要重定向这些)
|
||||
// 这里继承父进程的标准输入输出,实际使用时可能需要:
|
||||
// 1. 重定向到文件
|
||||
// 2. 指向 /dev/null
|
||||
// 3. 使用日志系统
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// 异步启动新进程(Start 而非 Run)
|
||||
// 注意:此处未设置新进程组或会话,可能导致子进程随终端关闭
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("无法启动新实例: %v", err) // 启动失败时终止父进程
|
||||
}
|
||||
|
||||
// 输出提示信息(调试用,正式版可删除)
|
||||
fmt.Println("新实例已启动,当前进程将退出")
|
||||
|
||||
// 退出父进程,让子进程继续运行
|
||||
// 注意:此时子进程的父进程变为 init 进程(PID 1)
|
||||
os.Exit(0)
|
||||
}
|
47
main.go
Normal file
47
main.go
Normal file
@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 主循环,为每个端口映射启动一个代理
|
||||
func Loop(config Config) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// 启动 TCP 代理
|
||||
for tcp_listenAddr, tcp_targetAddr := range config.Global.LT {
|
||||
tcp_wg.Add(1)
|
||||
go StartTcpProxy(tcp_listenAddr, tcp_targetAddr)
|
||||
}
|
||||
|
||||
// 启动 UDP 代理
|
||||
for udp_listenAddr, udp_targetAddr := range config.Global.LU {
|
||||
udp_wg.Add(1)
|
||||
go StartUdpProxy(ctx, udp_listenAddr, udp_targetAddr)
|
||||
}
|
||||
|
||||
// 监听退出信号,触发 `cancel()` 让所有 Goroutine 退出
|
||||
WaitForExit(cancel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
daemon := flag.Bool("d", false, "守护进程模式") // 解析命令行参数,是否以守护进程模式运行
|
||||
config := flag.String("c", "4to6.conf", "指定配置文件") // 解析命令行参数,是否以守护进程模式运行
|
||||
flag.Parse()
|
||||
|
||||
if *daemon {
|
||||
Daemon() // 如果设置了-d参数,则进入守护进程模式
|
||||
}
|
||||
|
||||
LT, err := parseConfig(*config)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "读取配置错误: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
Loop(LT) // 启动所有代理服务
|
||||
|
||||
}
|
137
tcp.go
Normal file
137
tcp.go
Normal file
@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
tcp_wg sync.WaitGroup
|
||||
tcp_listeners []net.Listener
|
||||
tcp_listenMux sync.Mutex // 确保 tcp_listeners 操作是线程安全的
|
||||
)
|
||||
|
||||
// 启用 TCP KeepAlive
|
||||
func setKeepAlive(conn net.Conn) {
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.SetKeepAlive(true)
|
||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭写方向
|
||||
func closeWrite(conn net.Conn) {
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
tcpConn.CloseWrite()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理客户端连接,并将数据转发到目标服务器
|
||||
func HandleTcpConnection(clientConn net.Conn, targetAddr string) {
|
||||
defer clientConn.Close()
|
||||
|
||||
// 连接目标服务器,支持 IPv6
|
||||
serverConn, err := net.Dial("tcp", targetAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("无法连接到 %s: %v\n", targetAddr, err)
|
||||
return
|
||||
}
|
||||
defer serverConn.Close()
|
||||
|
||||
// 开启 TCP KeepAlive,防止连接过早断开
|
||||
setKeepAlive(clientConn)
|
||||
setKeepAlive(serverConn)
|
||||
|
||||
clientAddr := clientConn.RemoteAddr().String()
|
||||
fmt.Printf("连接 %s -> %s\n", clientAddr, targetAddr)
|
||||
|
||||
var cpWG sync.WaitGroup
|
||||
cpWG.Add(2)
|
||||
|
||||
// 客户端 -> 目标服务器
|
||||
go func() {
|
||||
defer cpWG.Done()
|
||||
_, err := io.Copy(serverConn, clientConn)
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
fmt.Printf("数据转发 %s -> %s 失败: %v\n", clientAddr, targetAddr, err)
|
||||
}
|
||||
closeWrite(serverConn)
|
||||
}()
|
||||
|
||||
// 目标服务器 -> 客户端
|
||||
go func() {
|
||||
defer cpWG.Done()
|
||||
_, err := io.Copy(clientConn, serverConn)
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
fmt.Printf("数据转发 %s <- %s 失败: %v\n", clientAddr, targetAddr, err)
|
||||
}
|
||||
closeWrite(clientConn)
|
||||
}()
|
||||
|
||||
cpWG.Wait()
|
||||
fmt.Printf("连接 %s 结束\n", clientAddr)
|
||||
}
|
||||
|
||||
// 启动代理服务器
|
||||
func StartTcpProxy(listenAddr, targetAddr string) {
|
||||
defer tcp_wg.Done()
|
||||
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("监听 %s 失败: %v\n", listenAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
tcp_listenMux.Lock()
|
||||
tcp_listeners = append(tcp_listeners, listener)
|
||||
tcp_listenMux.Unlock()
|
||||
|
||||
fmt.Printf("代理服务启动: %s -> %s\n", listenAddr, targetAddr)
|
||||
|
||||
for {
|
||||
clientConn, err := listener.Accept()
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
fmt.Printf("监听器 %s 已关闭\n", listenAddr)
|
||||
return
|
||||
}
|
||||
fmt.Printf("接受连接失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
go HandleTcpConnection(clientConn, targetAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听系统信号,以便优雅地关闭 TCP 和 UDP 代理服务
|
||||
func WaitForExit(cancel context.CancelFunc) {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-sigChan
|
||||
fmt.Println("\n收到终止信号,准备关闭代理服务器...")
|
||||
|
||||
// 关闭所有 TCP 监听器
|
||||
for _, listener := range tcp_listeners {
|
||||
if err := listener.Close(); err != nil {
|
||||
fmt.Printf("关闭 TCP 监听器 %s 失败: %v\n", listener.Addr(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// 触发 UDP 代理退出
|
||||
cancel()
|
||||
|
||||
// 等待所有 TCP 和 UDP 代理退出
|
||||
tcp_wg.Wait()
|
||||
udp_wg.Wait()
|
||||
|
||||
fmt.Println("所有代理已安全退出")
|
||||
os.Exit(0)
|
||||
}
|
124
udp.go
Normal file
124
udp.go
Normal file
@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
udp_wg sync.WaitGroup
|
||||
clientMap sync.Map // 代替 sync.Mutex 保护的 map[string]*net.UDPConn
|
||||
)
|
||||
|
||||
// 处理 UDP 数据转发
|
||||
func handleUdpTraffic(listener *net.UDPConn, targetConn *net.UDPConn, clientAddr *net.UDPAddr, data []byte) {
|
||||
// 目标服务器超时设置,防止阻塞
|
||||
targetConn.SetWriteDeadline(time.Now().Add(2 * time.Second))
|
||||
_, err := targetConn.Write(data)
|
||||
if err != nil {
|
||||
fmt.Printf("UDP 数据转发失败 %s -> %s: %v\n", clientAddr, targetConn.RemoteAddr(), err)
|
||||
return
|
||||
}
|
||||
|
||||
// 读取目标服务器响应
|
||||
targetConn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
buffer := make([]byte, 4096)
|
||||
n, _, err := targetConn.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
fmt.Printf("UDP 读取目标服务器响应失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送响应回客户端
|
||||
listener.SetWriteDeadline(time.Now().Add(2 * time.Second))
|
||||
_, err = listener.WriteToUDP(buffer[:n], clientAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("UDP 发送响应失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 启动 UDP 代理
|
||||
func StartUdpProxy(ctx context.Context, listenAddr, targetAddr string) {
|
||||
defer udp_wg.Done()
|
||||
|
||||
localAddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("解析本地 UDP 地址 %s 失败: %v\n", listenAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := net.ListenUDP("udp", localAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("监听 UDP %s 失败: %v\n", listenAddr, err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
targetUDPAddr, err := net.ResolveUDPAddr("udp", targetAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("解析目标 UDP 地址 %s 失败: %v\n", targetAddr, err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("UDP 代理启动: %s -> %s\n", listenAddr, targetAddr)
|
||||
|
||||
// 设置多个 goroutine 处理 UDP 请求,提高吞吐量
|
||||
numWorkers := 4 // 线程数,可根据 CPU 资源调整
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
udp_wg.Add(1)
|
||||
go func() {
|
||||
defer udp_wg.Done()
|
||||
buffer := make([]byte, 4096)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
listener.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
n, clientAddr, err := listener.ReadFromUDP(buffer)
|
||||
if err != nil {
|
||||
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
||||
continue
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("UDP 读取数据失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 查找或创建目标连接
|
||||
clientKey := clientAddr.String()
|
||||
targetConn, exists := clientMap.Load(clientKey)
|
||||
if !exists {
|
||||
targetConn, err = net.DialUDP("udp", nil, targetUDPAddr)
|
||||
if err != nil {
|
||||
fmt.Printf("连接 UDP 目标 %s 失败: %v\n", targetAddr, err)
|
||||
continue
|
||||
}
|
||||
clientMap.Store(clientKey, targetConn)
|
||||
}
|
||||
|
||||
// 处理 UDP 数据
|
||||
go handleUdpTraffic(listener, targetConn.(*net.UDPConn), clientAddr, buffer[:n])
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// 等待退出信号
|
||||
<-ctx.Done()
|
||||
fmt.Println("\n收到退出信号,正在关闭 UDP 代理...")
|
||||
|
||||
listener.Close()
|
||||
clientMap.Range(func(key, value interface{}) bool {
|
||||
value.(*net.UDPConn).Close()
|
||||
return true
|
||||
})
|
||||
|
||||
fmt.Println("UDP 代理已关闭")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user