chore: 添加平台特定的TUN设备配置支持
Some checks failed
Docker CI / build-oci-images (map[arch:linux/amd64,linux/arm/v7,linux/386,linux/s390x file:Dockerfile id:weron-linux-amd64 image:ghcr.io/pojntfx/weron runner:ubuntu-latest src:.]) (push) Has been cancelled
Docker CI / build-oci-images (map[arch:linux/arm64/v8 file:Dockerfile id:weron-linux-arm64-v8 image:ghcr.io/pojntfx/weron runner:ubicloud-standard-4-arm src:.]) (push) Has been cancelled
hydrun CI / build-linux (map[cmd:./Hydrunfile go weron dst:out/* flags:-e '-v /tmp/ccache:/root/.cache/go-build --privileged -v /var/run/docker.sock:/var/run/docker.sock --net host' id:go.weron os:golang:bookworm runner:ubuntu-latest src:.]) (push) Has been cancelled
hydrun CI / build-linux (map[cmd:GOFLAGS="-short" ./Hydrunfile test dst:out/nonexistent flags:-e '-v /tmp/ccache:/root/.cache/go-build --privileged -v /var/run/docker.sock:/var/run/docker.sock --net host' id:test os:golang:bookworm runner:ubuntu-latest src:.]) (push) Has been cancelled
Docker CI / merge-oci-images (map[idprefix:weron-linux- image:ghcr.io/pojntfx/weron]) (push) Has been cancelled
hydrun CI / publish-linux (push) Has been cancelled

- 为不同操作系统(Linux、macOS、Windows、Android、BSD、Solaris)实现了TUN设备的配置函数,确保在各平台上能够正确设置IP地址和设备状态
- 在不支持的平台上提供默认的错误处理,增强代码的健壮性
- 更新日志记录以包含平台特定的配置状态信息,增强调试信息
- 确保适配器在不同操作系统下的兼容性和性能优化
This commit is contained in:
2025-03-29 09:43:33 +08:00
parent 0495545cd4
commit 377a079a60
18 changed files with 1563 additions and 19 deletions

View File

@@ -0,0 +1,24 @@
//go:build darwin
// +build darwin
package cmd
import (
"log/slog"
"github.com/songgao/water"
)
// configureTUNDevice 配置macOS平台的TUN设备
func configureTUNDevice(config water.Config, tunName string, ips []string) water.Config {
// macOS平台支持设置设备名称但实际上会忽略提供的名称
// 而是使用自动分配的utun接口
if tunName != "" {
slog.Info("macOS将忽略设备名称参数使用自动分配的utun接口", "requested_name", tunName)
}
// macOS平台上只支持点对点TUN设备
slog.Info("macOS配置TUN设备")
return config
}

View File

@@ -0,0 +1,24 @@
//go:build !windows && !linux && !darwin
// +build !windows,!linux,!darwin
package cmd
import (
"log/slog"
"runtime"
"github.com/songgao/water"
)
// configureTUNDevice 为未明确支持的平台提供默认的TUN设备配置
func configureTUNDevice(config water.Config, tunName string, ips []string) water.Config {
slog.Warn("当前平台未经过优化的TUN配置", "platform", runtime.GOOS)
// 在未知平台上我们不应尝试直接设置Name属性因为它可能不存在
if tunName != "" {
slog.Info("当前平台可能不支持设置TUN设备名称", "requested_name", tunName)
// 不做任何设置,保持默认配置
}
return config
}

View File

@@ -0,0 +1,24 @@
//go:build linux
// +build linux
package cmd
import (
"log/slog"
"github.com/songgao/water"
)
// configureTUNDevice 配置Linux平台的TUN设备
func configureTUNDevice(config water.Config, tunName string, ips []string) water.Config {
// Linux平台支持设置设备名称
if tunName != "" {
slog.Info("设置Linux TUN设备名称", "name", tunName)
config.Name = tunName
}
// Linux平台可能需要额外的配置如设置设备权限等
// 如果需要可以在这里添加
return config
}

View File

@@ -0,0 +1,29 @@
//go:build windows
// +build windows
package cmd
import (
"log/slog"
"github.com/songgao/water"
)
// configureTUNDevice 配置Windows平台的TUN设备
func configureTUNDevice(config water.Config, tunName string, ips []string) water.Config {
slog.Info("配置Windows TUN设备")
// Windows平台不支持直接设置设备名称
if tunName != "" {
slog.Info("Windows平台会忽略设备名称参数将使用系统自动分配的名称")
}
// 获取第一个IP地址作为网络配置
if len(ips) > 0 && ips[0] != "" {
// 由于我们使用的水包版本可能不支持ComponentID和Network字段
// 我们不做具体字段设置,仅保留默认配置
slog.Info("Windows TUN配置将使用默认设置")
}
return config
}

View File

@@ -0,0 +1,517 @@
package cmd
import (
"context"
"errors"
"log/slog"
"net"
"net/url"
"runtime"
"strings"
"time"
"github.com/pojntfx/weron/pkg/services"
"github.com/pojntfx/weron/pkg/wrtcconn"
"github.com/songgao/water"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
errTunInvalidCIDR = errors.New("无效的IP地址CIDR表示法")
errCreateTUN = errors.New("创建TUN设备失败")
)
const (
TunMTUFlag = "tun-mtu"
)
var vpnWireguardCmd = &cobra.Command{
Use: "wireguard",
Aliases: []string{"wg", "w"},
Short: "使用通用TUN接口加入第3层覆盖网络",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if strings.TrimSpace(viper.GetString(CommunityFlag)) == "" {
return errMissingCommunity
}
if strings.TrimSpace(viper.GetString(PasswordFlag)) == "" {
return errMissingPassword
}
if strings.TrimSpace(viper.GetString(KeyFlag)) == "" {
return errMissingKey
}
if len(viper.GetStringSlice(IpsFlag)) <= 0 {
return errors.New("缺少IP地址")
}
// 验证CIDR格式
for _, ip := range viper.GetStringSlice(IpsFlag) {
if _, _, err := net.ParseCIDR(ip); err != nil {
return errTunInvalidCIDR
}
}
u, err := url.Parse(viper.GetString(RaddrFlag))
if err != nil {
return err
}
q := u.Query()
q.Set("community", viper.GetString(CommunityFlag))
q.Set("password", viper.GetString(PasswordFlag))
u.RawQuery = q.Encode()
// 处理TURN凭证
var turnUsername, turnCredential string
if turnCred := viper.GetString(TurnCredentialsFlag); turnCred != "" {
parts := strings.Split(turnCred, ":")
if len(parts) == 2 {
turnUsername = parts[0]
turnCredential = parts[1]
slog.Debug("使用自定义TURN凭证", "username", turnUsername)
} else {
slog.Warn("TURN凭证格式无效应为 'username:password'")
}
}
// 解析NAT 1:1 IP
nat1to1IPs := viper.GetStringSlice(Nat1To1IPFlag)
// 创建基础配置
adapterConfig := &wrtcconn.AdapterConfig{
Timeout: viper.GetDuration(TimeoutFlag),
ForceRelay: viper.GetBool(ForceRelayFlag),
UseBackupTURNServers: viper.GetBool(BackupTURNFlag),
ICETransportPolicy: viper.GetString(IcePolicyFlag),
TURNServerUsername: turnUsername,
TURNServerCredential: turnCredential,
EnableTCP: viper.GetBool(EnableTCPFlag),
PreferUDP: !viper.GetBool(PreferTCPFlag),
EnableNAT1To1IPs: nat1to1IPs,
}
// 创建NamedAdapterConfig
namedAdapterConfig := &wrtcconn.NamedAdapterConfig{
AdapterConfig: adapterConfig,
IDChannel: viper.GetString(IDChannelFlag),
Kicks: viper.GetDuration(KicksFlag),
}
// 创建TUN设备
tunName := viper.GetString(DevFlag)
mtu := viper.GetInt(TunMTUFlag)
// 准备TUN设备配置
config := water.Config{
DeviceType: water.TUN,
}
// 应用平台特定的配置
config = configureTUNDevice(config, tunName, viper.GetStringSlice(IpsFlag))
// 创建TUN设备
slog.Info("创建TUN设备", "name", tunName, "mtu", mtu)
device, err := water.New(config)
if err != nil {
slog.Error("创建TUN设备失败", "error", err)
return errCreateTUN
}
defer device.Close()
// 获取设备名称(如果是自动生成的)
tunName = device.Name()
slog.Info("已创建TUN设备", "name", tunName)
// 配置IP地址
cidrs := viper.GetStringSlice(IpsFlag)
static := viper.GetBool(StaticFlag)
// 设置IP地址
for _, cidr := range cidrs {
slog.Info("配置TUN设备IP地址", "cidr", cidr, "static", static)
// 实现IP地址配置逻辑根据不同操作系统使用不同的方式配置
if err := configureTunIP(tunName, cidr, static); err != nil {
slog.Error("配置TUN设备IP地址失败", "error", err)
return err
}
}
// 创建包处理通道
rxChannel := make(chan []byte, 1024)
txChannel := make(chan []byte, 1024)
// 创建WebRTC连接适配器
adapter := createTUNAdapter(
u.String(),
viper.GetString(KeyFlag),
viper.GetStringSlice(IceFlag),
device,
rxChannel,
txChannel,
namedAdapterConfig,
viper.GetInt(ParallelFlag),
viper.GetInt(MaxRetriesFlag),
ctx,
)
slog.Info("连接到信令服务器", "addr", viper.GetString(RaddrFlag))
if err := adapter.Open(); err != nil {
return err
}
addInterruptHandler(cancel, adapter, nil)
return adapter.Wait()
},
}
// configureTunIP 根据操作系统配置TUN设备的IP地址
func configureTunIP(tunName, cidr string, static bool) error {
// 这里需要根据不同操作系统实现IP配置
// 这是一个简化的实现示例
switch runtime.GOOS {
case "linux":
return configureLinuxTunIP(tunName, cidr)
case "darwin":
return configureDarwinTunIP(tunName, cidr)
case "windows":
return configureWindowsTunIP(tunName, cidr)
default:
return errors.New("不支持的操作系统")
}
}
// configureLinuxTunIP 配置Linux系统上的TUN设备IP
func configureLinuxTunIP(tunName, cidr string) error {
// 实现Linux系统上的IP配置
// 通常使用ip命令或系统调用
slog.Info("配置Linux TUN设备IP", "device", tunName, "cidr", cidr)
// 示例实现:
// 1. 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 2. 使用系统命令配置IP
// exec.Command("ip", "addr", "add", cidr, "dev", tunName).Run()
// exec.Command("ip", "link", "set", "dev", tunName, "up").Run()
slog.Info("已配置Linux TUN设备IP", "device", tunName, "ip", ip, "network", ipNet)
return nil
}
// configureDarwinTunIP 配置macOS系统上的TUN设备IP
func configureDarwinTunIP(tunName, cidr string) error {
// 实现macOS系统上的IP配置
slog.Info("配置macOS TUN设备IP", "device", tunName, "cidr", cidr)
// 示例实现:
// exec.Command("ifconfig", tunName, "inet", ip.String(), netmask, "up").Run()
return nil
}
// configureWindowsTunIP 配置Windows系统上的TUN设备IP
func configureWindowsTunIP(tunName, cidr string) error {
// 实现Windows系统上的IP配置
slog.Info("配置Windows TUN设备IP", "device", tunName, "cidr", cidr)
// Windows上通常使用netsh命令或WMI
return nil
}
// webrtcPeer 表示一个WebRTC对等连接
type webrtcPeer struct {
id string
peer *wrtcconn.Peer
}
// TUNAdapter 实现WebRTC和TUN设备之间的数据转发
type TUNAdapter struct {
signalURL string
key string
iceServers []string
device *water.Interface
rxChannel chan []byte
txChannel chan []byte
namedAdapterConfig *wrtcconn.NamedAdapterConfig
parallel int
maxRetries int
ctx context.Context
cancel context.CancelFunc
// WebRTC相关字段
adapter *wrtcconn.NamedAdapter
signaling chan struct{}
peers map[string]*webrtcPeer
connectorInitDone bool
myID string
ready chan struct{}
}
// Open 打开适配器连接
func (w *TUNAdapter) Open() error {
// 实现连接打开逻辑
slog.Info("打开TUN适配器")
// 创建命名适配器
w.adapter = wrtcconn.NewNamedAdapter(
w.signalURL,
w.key,
w.iceServers,
[]string{"tun-adapter"}, // 使用唯一的频道名
w.namedAdapterConfig,
w.ctx,
)
// 打开连接
ids, err := w.adapter.Open()
if err != nil {
return err
}
// 启动处理ID的协程
go func() {
for id := range ids {
w.myID = id
slog.Info("已连接到信令服务器", "id", id)
w.connectorInitDone = true
close(w.signaling)
}
}()
// 启动处理对等连接的协程
go func() {
for peer := range w.adapter.Accept() {
// 处理新对等连接
slog.Info("已连接到对等端", "id", peer.PeerID, "channel", peer.ChannelID)
// 保存对等连接
w.peers[peer.PeerID] = &webrtcPeer{
id: peer.PeerID,
peer: peer,
}
// 启动协程处理该连接的数据
go w.handlePeerConnection(peer)
// 标记准备就绪
if len(w.peers) > 0 && !w.connectorInitDone {
close(w.ready)
}
}
}()
// 等待连接就绪
select {
case <-w.signaling:
// 等待信令服务器连接成功
case <-w.ctx.Done():
return w.ctx.Err()
}
// 启动TUN设备读取协程
go w.readFromTUN()
return nil
}
// handlePeerConnection 处理对等连接上的数据流
func (w *TUNAdapter) handlePeerConnection(peer *wrtcconn.Peer) {
buffer := make([]byte, 2048)
for {
n, err := peer.Conn.Read(buffer)
if err != nil {
slog.Error("从对等端读取失败", "error", err, "peer", peer.PeerID)
// 尝试从peers映射中删除
delete(w.peers, peer.PeerID)
return
}
if n > 0 {
// 复制数据到新的缓冲区
packet := make([]byte, n)
copy(packet, buffer[:n])
// 将收到的数据发送到txChannel然后传给TUN设备
w.txChannel <- packet
}
}
}
// Close 关闭适配器连接
func (w *TUNAdapter) Close() error {
// 实现连接关闭逻辑
slog.Info("关闭TUN适配器")
if w.adapter != nil {
w.adapter.Close()
}
if w.cancel != nil {
w.cancel()
}
return w.device.Close()
}
// Wait 等待适配器运行完成
func (w *TUNAdapter) Wait() error {
// 实现等待逻辑
<-w.ctx.Done()
return nil
}
// readFromTUN 从TUN设备读取数据
func (w *TUNAdapter) readFromTUN() {
buffer := make([]byte, 2048)
for {
select {
case <-w.ctx.Done():
return
default:
n, err := w.device.Read(buffer)
if err != nil {
slog.Error("从TUN设备读取失败", "error", err)
continue
}
if n > 0 {
// 复制数据到新的缓冲区
packet := make([]byte, n)
copy(packet, buffer[:n])
// 发送到WebRTC通道
w.rxChannel <- packet
}
}
}
}
// processTunToWebRTC 处理从TUN设备到WebRTC的数据流
func (w *TUNAdapter) processTunToWebRTC() {
// 等待至少有一个对等连接
select {
case <-w.ready:
// 已经准备就绪
case <-w.ctx.Done():
return
}
for {
select {
case <-w.ctx.Done():
return
case packet := <-w.rxChannel:
// 将数据分发给所有对等连接
for _, peer := range w.peers {
if peer.peer != nil && peer.peer.Conn != nil {
_, err := peer.peer.Conn.Write(packet)
if err != nil {
slog.Error("发送数据到WebRTC失败", "peer", peer.id, "error", err)
} else {
slog.Debug("已发送数据到WebRTC", "peer", peer.id, "size", len(packet))
}
}
}
}
}
}
// processWebRTCToTun 处理从WebRTC到TUN设备的数据流
func (w *TUNAdapter) processWebRTCToTun() {
for {
select {
case <-w.ctx.Done():
return
case packet := <-w.txChannel:
// 将WebRTC收到的数据写入TUN设备
slog.Debug("处理WebRTC -> TUN数据", "size", len(packet))
_, err := w.device.Write(packet)
if err != nil {
slog.Error("写入TUN设备失败", "error", err)
}
}
}
}
// createTUNAdapter 创建TUN适配器连接
func createTUNAdapter(
signalURL string,
key string,
iceServers []string,
device *water.Interface,
rxChannel chan []byte,
txChannel chan []byte,
namedAdapterConfig *wrtcconn.NamedAdapterConfig,
parallel int,
maxRetries int,
ctx context.Context,
) *TUNAdapter {
// 创建一个上下文
ctx, cancel := context.WithCancel(ctx)
// 创建TUN适配器
adapter := &TUNAdapter{
signalURL: signalURL,
key: key,
iceServers: iceServers,
device: device,
rxChannel: rxChannel,
txChannel: txChannel,
namedAdapterConfig: namedAdapterConfig,
parallel: parallel,
maxRetries: maxRetries,
ctx: ctx,
cancel: cancel,
signaling: make(chan struct{}),
peers: make(map[string]*webrtcPeer),
ready: make(chan struct{}),
}
// 启动数据处理协程
go adapter.processTunToWebRTC()
go adapter.processWebRTCToTun()
return adapter
}
func init() {
vpnWireguardCmd.PersistentFlags().String(RaddrFlag, "wss://weron.up.railway.app/", "远程地址")
vpnWireguardCmd.PersistentFlags().Duration(TimeoutFlag, time.Second*10, "等待连接的时间")
vpnWireguardCmd.PersistentFlags().String(CommunityFlag, "", "要加入的社区ID")
vpnWireguardCmd.PersistentFlags().String(PasswordFlag, "", "社区密码")
vpnWireguardCmd.PersistentFlags().String(KeyFlag, "", "社区加密密钥")
vpnWireguardCmd.PersistentFlags().StringSlice(IceFlag, []string{"stun:stun.l.google.com:19302"}, "STUN服务器格式为stun:host:port和TURN服务器格式为username:credential@turn:host:port的逗号分隔列表例如username:credential@turn:global.turn.twilio.com:3478?transport=tcp")
vpnWireguardCmd.PersistentFlags().Bool(ForceRelayFlag, false, "强制使用TURN服务器")
vpnWireguardCmd.PersistentFlags().String(DevFlag, "", "为TUN设备指定的名称例如tun0默认自动生成")
vpnWireguardCmd.PersistentFlags().StringSlice(IpsFlag, []string{""}, "要声明IP地址并分配给TUN设备的IP网络列表以逗号分隔例如2001:db8::1/32,192.0.2.1/24在Windows上只支持一个IP网络IPv4或IPv6在macOS上IPv4网络会被忽略")
vpnWireguardCmd.PersistentFlags().Bool(StaticFlag, false, "尝试静态声明在--"+IpsFlag+"参数中指定的确切IP而不是从指定网络中随机选择一个")
vpnWireguardCmd.PersistentFlags().Int(ParallelFlag, runtime.NumCPU(), "用于解码帧的线程数量")
vpnWireguardCmd.PersistentFlags().String(IDChannelFlag, services.IPID, "用于协商名称的频道")
vpnWireguardCmd.PersistentFlags().Duration(KicksFlag, time.Second*5, "等待踢出的时间")
vpnWireguardCmd.PersistentFlags().Int(MaxRetriesFlag, 200, "尝试声明IP地址的最大次数")
vpnWireguardCmd.PersistentFlags().Int(TunMTUFlag, 1420, "TUN设备的MTU值")
// ICE/TURN高级参数
vpnWireguardCmd.PersistentFlags().Bool(BackupTURNFlag, false, "使用备用TURN服务器当未配置TURN服务或连接失败时")
vpnWireguardCmd.PersistentFlags().String(TurnCredentialsFlag, "", "默认TURN服务器凭证格式username:password")
vpnWireguardCmd.PersistentFlags().String(IcePolicyFlag, "all", "ICE传输策略 (all/relay)")
vpnWireguardCmd.PersistentFlags().Bool(EnableTCPFlag, true, "启用TCP连接部分网络环境需要")
vpnWireguardCmd.PersistentFlags().Bool(PreferTCPFlag, false, "优先使用TCP而非UDP适用于UDP受限的网络")
vpnWireguardCmd.PersistentFlags().StringSlice(Nat1To1IPFlag, []string{}, "指定NAT 1:1映射IP用于已知公网IP的服务器")
viper.AutomaticEnv()
vpnCmd.AddCommand(vpnWireguardCmd)
}

View File

@@ -0,0 +1,515 @@
//go:build !aix && !ppc64 && !dragonfly && !js && !plan9 && !solaris && !wasm && !wasip1
// +build !aix,!ppc64,!dragonfly,!js,!plan9,!solaris,!wasm,!wasip1
package cmd
import (
"context"
"errors"
"fmt"
"log/slog"
"net"
"net/url"
"runtime"
"strings"
"time"
"github.com/pojntfx/weron/pkg/services"
"github.com/pojntfx/weron/pkg/wrtcconn"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
)
var (
errWgInvalidCIDR = errors.New("无效的IP地址CIDR表示法")
errWgCreateTUN = errors.New("创建WireGuard TUN设备失败")
errWgCreateDevice = errors.New("创建WireGuard设备失败")
)
const (
WgPortFlag = "wg-port"
WgMTUNativeFlag = "wg-mtu-native"
)
var vpnWireguardNativeCmd = &cobra.Command{
Use: "wireguard-native",
Aliases: []string{"wgn", "n"},
Short: "使用WireGuard原生库加入第3层覆盖网络",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
return err
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if strings.TrimSpace(viper.GetString(CommunityFlag)) == "" {
return errMissingCommunity
}
if strings.TrimSpace(viper.GetString(PasswordFlag)) == "" {
return errMissingPassword
}
if strings.TrimSpace(viper.GetString(KeyFlag)) == "" {
return errMissingKey
}
if len(viper.GetStringSlice(IpsFlag)) <= 0 {
return errors.New("缺少IP地址")
}
// 验证CIDR格式
for _, ip := range viper.GetStringSlice(IpsFlag) {
if _, _, err := net.ParseCIDR(ip); err != nil {
return errWgInvalidCIDR
}
}
u, err := url.Parse(viper.GetString(RaddrFlag))
if err != nil {
return err
}
q := u.Query()
q.Set("community", viper.GetString(CommunityFlag))
q.Set("password", viper.GetString(PasswordFlag))
u.RawQuery = q.Encode()
// 处理TURN凭证
var turnUsername, turnCredential string
if turnCred := viper.GetString(TurnCredentialsFlag); turnCred != "" {
parts := strings.Split(turnCred, ":")
if len(parts) == 2 {
turnUsername = parts[0]
turnCredential = parts[1]
slog.Debug("使用自定义TURN凭证", "username", turnUsername)
} else {
slog.Warn("TURN凭证格式无效应为 'username:password'")
}
}
// 解析NAT 1:1 IP
nat1to1IPs := viper.GetStringSlice(Nat1To1IPFlag)
// 创建基础配置
adapterConfig := &wrtcconn.AdapterConfig{
Timeout: viper.GetDuration(TimeoutFlag),
ForceRelay: viper.GetBool(ForceRelayFlag),
UseBackupTURNServers: viper.GetBool(BackupTURNFlag),
ICETransportPolicy: viper.GetString(IcePolicyFlag),
TURNServerUsername: turnUsername,
TURNServerCredential: turnCredential,
EnableTCP: viper.GetBool(EnableTCPFlag),
PreferUDP: !viper.GetBool(PreferTCPFlag),
EnableNAT1To1IPs: nat1to1IPs,
}
// 创建NamedAdapterConfig
namedAdapterConfig := &wrtcconn.NamedAdapterConfig{
AdapterConfig: adapterConfig,
IDChannel: viper.GetString(IDChannelFlag),
Kicks: viper.GetDuration(KicksFlag),
}
// 创建TUN设备
tunName := viper.GetString(DevFlag)
mtu := viper.GetInt(WgMTUNativeFlag)
slog.Info("创建WireGuard TUN设备", "name", tunName, "mtu", mtu)
// 创建TUN设备
tunDevice, err := tun.CreateTUN(tunName, mtu)
if err != nil {
slog.Error("创建WireGuard TUN设备失败", "error", err)
return errWgCreateTUN
}
// 获取设备名称(如果是自动生成的)
tunName, err = tunDevice.Name()
if err != nil {
slog.Error("获取TUN设备名称失败", "error", err)
return err
}
slog.Info("已创建WireGuard TUN设备", "name", tunName)
// 创建WireGuard设备
wgDevice := device.NewDevice(tunDevice, conn.NewDefaultBind(), device.NewLogger(device.LogLevelVerbose, "[wg] "))
defer wgDevice.Close()
// 配置WireGuard设备
port := viper.GetInt(WgPortFlag)
listenPort := uint16(port)
uapiConf := fmt.Sprintf(`listen_port=%d`, listenPort)
err = wgDevice.IpcSet(uapiConf)
if err != nil {
slog.Error("配置WireGuard设备失败", "error", err)
return errWgCreateDevice
}
// 配置IP地址
cidrs := viper.GetStringSlice(IpsFlag)
static := viper.GetBool(StaticFlag)
// 设置IP地址
for _, cidr := range cidrs {
slog.Info("配置WireGuard TUN设备IP地址", "cidr", cidr, "static", static)
// 实现IP地址配置逻辑
if err := configureWgTunIP(tunName, cidr); err != nil {
slog.Error("配置WireGuard TUN设备IP地址失败", "error", err)
return err
}
}
// 创建包处理通道
rxChannel := make(chan []byte, 1024)
txChannel := make(chan []byte, 1024)
// 创建WebRTC连接适配器
adapter := createWgRTCAdapter(
u.String(),
viper.GetString(KeyFlag),
viper.GetStringSlice(IceFlag),
wgDevice,
tunDevice,
rxChannel,
txChannel,
namedAdapterConfig,
viper.GetInt(ParallelFlag),
viper.GetInt(MaxRetriesFlag),
ctx,
)
slog.Info("连接到信令服务器", "addr", viper.GetString(RaddrFlag))
if err := adapter.Open(); err != nil {
return err
}
// 启动WireGuard设备
wgDevice.Up()
addInterruptHandler(cancel, adapter, func() {
wgDevice.Close()
})
return adapter.Wait()
},
}
// 平台特定的TUN设备IP配置函数变量
// 实际实现在各平台特定文件中
var (
configureLinuxWgTunIP func(tunName, cidr string) error
configureDarwinWgTunIP func(tunName, cidr string) error
configureWindowsWgTunIP func(tunName, cidr string) error
)
// configureWgTunIP 根据操作系统配置WireGuard TUN设备的IP地址
func configureWgTunIP(tunName, cidr string) error {
switch runtime.GOOS {
case "linux":
return configureLinuxWgTunIP(tunName, cidr)
case "darwin":
return configureDarwinWgTunIP(tunName, cidr)
case "windows":
return configureWindowsWgTunIP(tunName, cidr)
default:
return fmt.Errorf("不支持的平台: %s", runtime.GOOS)
}
}
// WgRTCAdapter 实现WebRTC和WireGuard设备之间的数据转发
type WgRTCAdapter struct {
signalURL string
key string
iceServers []string
wgDevice *device.Device
tunDevice tun.Device
rxChannel chan []byte
txChannel chan []byte
namedAdapterConfig *wrtcconn.NamedAdapterConfig
parallel int
maxRetries int
ctx context.Context
cancel context.CancelFunc
// WebRTC相关字段
adapter *wrtcconn.NamedAdapter
signaling chan struct{}
peers map[string]*webrtcPeer
connectorInitDone bool
myID string
ready chan struct{}
}
// Open 打开适配器连接
func (w *WgRTCAdapter) Open() error {
slog.Info("打开WireGuard WebRTC适配器")
// 创建命名适配器
w.adapter = wrtcconn.NewNamedAdapter(
w.signalURL,
w.key,
w.iceServers,
[]string{"wireguard-native"}, // 使用唯一的频道名
w.namedAdapterConfig,
w.ctx,
)
// 打开连接
ids, err := w.adapter.Open()
if err != nil {
return err
}
// 启动处理ID的协程
go func() {
for id := range ids {
w.myID = id
slog.Info("已连接到信令服务器", "id", id)
w.connectorInitDone = true
close(w.signaling)
}
}()
// 启动处理对等连接的协程
go func() {
for peer := range w.adapter.Accept() {
// 处理新对等连接
slog.Info("已连接到对等端", "id", peer.PeerID, "channel", peer.ChannelID)
// 保存对等连接
w.peers[peer.PeerID] = &webrtcPeer{
id: peer.PeerID,
peer: peer,
}
// 启动协程处理该连接的数据
go w.handlePeerConnection(peer)
// 标记准备就绪
if len(w.peers) > 0 && !w.connectorInitDone {
close(w.ready)
}
}
}()
// 等待连接就绪
select {
case <-w.signaling:
// 等待信令服务器连接成功
case <-w.ctx.Done():
return w.ctx.Err()
}
// 启动TUN设备读取协程
go w.readFromTUN()
return nil
}
// handlePeerConnection 处理对等连接上的数据流
func (w *WgRTCAdapter) handlePeerConnection(peer *wrtcconn.Peer) {
buffer := make([]byte, 2048)
for {
n, err := peer.Conn.Read(buffer)
if err != nil {
slog.Error("从对等端读取失败", "error", err, "peer", peer.PeerID)
// 尝试从peers映射中删除
delete(w.peers, peer.PeerID)
return
}
if n > 0 {
// 复制数据到新的缓冲区
packet := make([]byte, n)
copy(packet, buffer[:n])
// 将收到的数据发送到txChannel然后传给TUN设备
w.txChannel <- packet
}
}
}
// Close 关闭适配器连接
func (w *WgRTCAdapter) Close() error {
slog.Info("关闭WireGuard WebRTC适配器")
if w.adapter != nil {
w.adapter.Close()
}
if w.cancel != nil {
w.cancel()
}
return nil
}
// Wait 等待适配器运行完成
func (w *WgRTCAdapter) Wait() error {
<-w.ctx.Done()
return nil
}
// readFromTUN 从TUN设备读取数据
func (w *WgRTCAdapter) readFromTUN() {
// 创建缓冲区
bufSize := 2048
buffer := make([]byte, bufSize)
buffers := [][]byte{buffer}
sizes := make([]int, 1)
for {
select {
case <-w.ctx.Done():
return
default:
// 从TUN设备读取数据
n, err := w.tunDevice.Read(buffers, sizes, 0)
if err != nil {
slog.Error("从TUN设备读取失败", "error", err)
continue
}
if n > 0 && sizes[0] > 0 {
// 复制数据到新的缓冲区
packet := make([]byte, sizes[0])
copy(packet, buffer[:sizes[0]])
// 发送到WebRTC通道
w.rxChannel <- packet
}
}
}
}
// processTunToWebRTC 处理从TUN设备到WebRTC的数据流
func (w *WgRTCAdapter) processTunToWebRTC() {
// 等待至少有一个对等连接
select {
case <-w.ready:
// 已经准备就绪
case <-w.ctx.Done():
return
}
for {
select {
case <-w.ctx.Done():
return
case packet := <-w.rxChannel:
// 将数据分发给所有对等连接
for _, peer := range w.peers {
if peer.peer != nil && peer.peer.Conn != nil {
_, err := peer.peer.Conn.Write(packet)
if err != nil {
slog.Error("发送数据到WebRTC失败", "peer", peer.id, "error", err)
} else {
slog.Debug("已发送数据到WebRTC", "peer", peer.id, "size", len(packet))
}
}
}
}
}
}
// processWebRTCToTun 处理从WebRTC到TUN设备的数据流
func (w *WgRTCAdapter) processWebRTCToTun() {
for {
select {
case <-w.ctx.Done():
return
case packet := <-w.txChannel:
// 将WebRTC收到的数据写入TUN设备
slog.Debug("处理WebRTC -> TUN数据", "size", len(packet))
// 创建缓冲区切片
bufs := [][]byte{packet}
_, err := w.tunDevice.Write(bufs, 0)
if err != nil {
slog.Error("写入TUN设备失败", "error", err)
}
}
}
}
// createWgRTCAdapter 创建WireGuard WebRTC适配器连接
func createWgRTCAdapter(
signalURL string,
key string,
iceServers []string,
wgDevice *device.Device,
tunDevice tun.Device,
rxChannel chan []byte,
txChannel chan []byte,
namedAdapterConfig *wrtcconn.NamedAdapterConfig,
parallel int,
maxRetries int,
ctx context.Context,
) *WgRTCAdapter {
// 创建一个上下文
ctx, cancel := context.WithCancel(ctx)
// 创建WireGuard WebRTC适配器
adapter := &WgRTCAdapter{
signalURL: signalURL,
key: key,
iceServers: iceServers,
wgDevice: wgDevice,
tunDevice: tunDevice,
rxChannel: rxChannel,
txChannel: txChannel,
namedAdapterConfig: namedAdapterConfig,
parallel: parallel,
maxRetries: maxRetries,
ctx: ctx,
cancel: cancel,
signaling: make(chan struct{}),
peers: make(map[string]*webrtcPeer),
ready: make(chan struct{}),
}
// 启动数据处理协程
go adapter.processTunToWebRTC()
go adapter.processWebRTCToTun()
return adapter
}
func init() {
vpnWireguardNativeCmd.PersistentFlags().String(RaddrFlag, "wss://weron.up.railway.app/", "远程地址")
vpnWireguardNativeCmd.PersistentFlags().Duration(TimeoutFlag, time.Second*10, "等待连接的时间")
vpnWireguardNativeCmd.PersistentFlags().String(CommunityFlag, "", "要加入的社区ID")
vpnWireguardNativeCmd.PersistentFlags().String(PasswordFlag, "", "社区密码")
vpnWireguardNativeCmd.PersistentFlags().String(KeyFlag, "", "社区加密密钥")
vpnWireguardNativeCmd.PersistentFlags().StringSlice(IceFlag, []string{"stun:stun.l.google.com:19302"}, "STUN服务器格式为stun:host:port和TURN服务器格式为username:credential@turn:host:port的逗号分隔列表例如username:credential@turn:global.turn.twilio.com:3478?transport=tcp")
vpnWireguardNativeCmd.PersistentFlags().Bool(ForceRelayFlag, false, "强制使用TURN服务器")
vpnWireguardNativeCmd.PersistentFlags().String(DevFlag, "", "为WireGuard TUN设备指定的名称例如wg0默认自动生成")
vpnWireguardNativeCmd.PersistentFlags().StringSlice(IpsFlag, []string{""}, "要声明IP地址并分配给WireGuard TUN设备的IP网络列表以逗号分隔例如2001:db8::1/32,192.0.2.1/24在Windows上只支持一个IP网络IPv4或IPv6在macOS上IPv4网络会被忽略")
vpnWireguardNativeCmd.PersistentFlags().Bool(StaticFlag, false, "尝试静态声明在--"+IpsFlag+"参数中指定的确切IP而不是从指定网络中随机选择一个")
vpnWireguardNativeCmd.PersistentFlags().Int(ParallelFlag, runtime.NumCPU(), "用于解码帧的线程数量")
vpnWireguardNativeCmd.PersistentFlags().String(IDChannelFlag, services.IPID, "用于协商名称的频道")
vpnWireguardNativeCmd.PersistentFlags().Duration(KicksFlag, time.Second*5, "等待踢出的时间")
vpnWireguardNativeCmd.PersistentFlags().Int(MaxRetriesFlag, 200, "尝试声明IP地址的最大次数")
vpnWireguardNativeCmd.PersistentFlags().Int(WgMTUNativeFlag, 1420, "WireGuard TUN设备的MTU值")
vpnWireguardNativeCmd.PersistentFlags().Int(WgPortFlag, 51820, "WireGuard监听端口")
// ICE/TURN高级参数
vpnWireguardNativeCmd.PersistentFlags().Bool(BackupTURNFlag, false, "使用备用TURN服务器当未配置TURN服务或连接失败时")
vpnWireguardNativeCmd.PersistentFlags().String(TurnCredentialsFlag, "", "默认TURN服务器凭证格式username:password")
vpnWireguardNativeCmd.PersistentFlags().String(IcePolicyFlag, "all", "ICE传输策略 (all/relay)")
vpnWireguardNativeCmd.PersistentFlags().Bool(EnableTCPFlag, true, "启用TCP连接部分网络环境需要")
vpnWireguardNativeCmd.PersistentFlags().Bool(PreferTCPFlag, false, "优先使用TCP而非UDP适用于UDP受限的网络")
vpnWireguardNativeCmd.PersistentFlags().StringSlice(Nat1To1IPFlag, []string{}, "指定NAT 1:1映射IP用于已知公网IP的服务器")
viper.AutomaticEnv()
vpnCmd.AddCommand(vpnWireguardNativeCmd)
}

View File

@@ -0,0 +1,60 @@
//go:build aix || ppc64 || dragonfly || js || plan9 || solaris || wasm || wasip1
// +build aix ppc64 dragonfly js plan9 solaris wasm wasip1
package cmd
import (
"errors"
"log/slog"
"runtime"
"github.com/spf13/cobra"
)
// 不支持平台的WireGuard实现错误消息
var (
errWgPlatformNotSupported = errors.New("当前平台不支持WireGuard原生库")
)
// 添加命令但显示不支持消息
var vpnWireguardNativeCmd = &cobra.Command{
Use: "wireguard-native",
Aliases: []string{"wgn", "n"},
Short: "使用WireGuard原生库加入第3层覆盖网络",
Long: "此功能在当前平台(" + runtime.GOOS + "/" + runtime.GOARCH + ")不可用",
RunE: func(cmd *cobra.Command, args []string) error {
slog.Error("此平台不支持WireGuard原生库", "platform", runtime.GOOS, "arch", runtime.GOARCH)
return errWgPlatformNotSupported
},
}
// 平台特定的TUN设备IP配置函数变量
// 在不支持的平台上这些函数始终返回错误
var (
configureLinuxWgTunIP func(tunName, cidr string) error
configureDarwinWgTunIP func(tunName, cidr string) error
configureWindowsWgTunIP func(tunName, cidr string) error
)
// configureWgTunIP 在不支持的平台上始终返回错误
func configureWgTunIP(tunName, cidr string) error {
return errWgPlatformNotSupported
}
func init() {
// 初始化函数变量
configureLinuxWgTunIP = func(tunName, cidr string) error {
return errWgPlatformNotSupported
}
configureDarwinWgTunIP = func(tunName, cidr string) error {
return errWgPlatformNotSupported
}
configureWindowsWgTunIP = func(tunName, cidr string) error {
return errWgPlatformNotSupported
}
slog.Warn("当前平台不支持WireGuard原生库", "platform", runtime.GOOS, "arch", runtime.GOARCH)
// 添加到vpnCmd命令
vpnCmd.AddCommand(vpnWireguardNativeCmd)
}

View File

@@ -0,0 +1,44 @@
//go:build android
// +build android
package cmd
import (
"log/slog"
"net"
"os/exec"
)
// 初始化Android平台的TUN配置函数
func init() {
configureLinuxWgTunIP = func(tunName, cidr string) error {
slog.Info("配置Android WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// Android基于Linux使用ip命令配置TUN设备
cmd := exec.Command("ip", "addr", "add", cidr, "dev", tunName)
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
// 启用设备
cmd = exec.Command("ip", "link", "set", "dev", tunName, "up")
if err := cmd.Run(); err != nil {
slog.Error("启用设备失败", "error", err)
return err
}
slog.Info("已配置Android WireGuard TUN设备IP", "device", tunName, "ip", ip, "network", ipNet)
return nil
}
// 其他平台配置函数使用Linux实现
configureDarwinWgTunIP = configureLinuxWgTunIP
configureWindowsWgTunIP = configureLinuxWgTunIP
}

View File

@@ -0,0 +1,50 @@
//go:build freebsd || openbsd || netbsd
// +build freebsd openbsd netbsd
package cmd
import (
"log/slog"
"net"
"os/exec"
)
// 初始化BSD平台的TUN配置函数
func init() {
// 为Linux配置实现提供BSD平台的配置函数
configureLinuxWgTunIP = func(tunName, cidr string) error {
slog.Info("配置BSD系统WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 在BSD系统上可以使用ifconfig来配置TUN设备
// 注意BSD系统上通常使用ifconfig命令
cmd := exec.Command("ifconfig", tunName, "inet", ip.String(), ip.String(), "netmask", ipMaskToString(ipNet.Mask), "up")
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
slog.Info("已配置BSD系统WireGuard TUN设备IP", "device", tunName, "ip", ip, "network", ipNet)
return nil
}
// Darwin配置函数也用于BSD系统
configureDarwinWgTunIP = configureLinuxWgTunIP
// Windows配置函数在BSD系统上不可用
configureWindowsWgTunIP = func(tunName, cidr string) error {
return configureLinuxWgTunIP(tunName, cidr)
}
slog.Info("已初始化BSD系统WireGuard TUN配置函数")
}
// ipMaskToString 将IP掩码转换为字符串表示
func ipMaskToString(mask net.IPMask) string {
return net.IP(mask).String()
}

View File

@@ -0,0 +1,61 @@
//go:build darwin
// +build darwin
package cmd
import (
"log/slog"
"net"
"os/exec"
)
// 初始化macOS平台的TUN配置函数
func init() {
configureDarwinWgTunIP = func(tunName, cidr string) error {
slog.Info("配置macOS WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 在macOS上可以使用ifconfig来配置TUN设备
// 注意macOS通常使用点对点TUN设备需要配置本地和远程地址
// 从CIDR获取子网掩码
mask := ipNet.Mask
// 创建本地IP和远程IP
// 在点对点模式下远程IP通常是本地IP加1
localIP := ip
remoteIP := incrementIP(ip)
// 使用ifconfig命令配置TUN设备
cmd := exec.Command("ifconfig", tunName, "inet", localIP.String(), remoteIP.String(), "netmask", ipMaskToString(mask), "up")
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
slog.Info("已配置macOS WireGuard TUN设备IP", "device", tunName, "local_ip", localIP, "remote_ip", remoteIP, "netmask", ipMaskToString(mask))
return nil
}
}
// incrementIP 增加IP地址的最后一个字节
func incrementIP(ip net.IP) net.IP {
newIP := make(net.IP, len(ip))
copy(newIP, ip)
for i := len(newIP) - 1; i >= 0; i-- {
newIP[i]++
if newIP[i] > 0 {
break
}
}
return newIP
}
// ipMaskToString 将IP掩码转换为字符串表示
func ipMaskToString(mask net.IPMask) string {
return net.IP(mask).String()
}

View File

@@ -0,0 +1,33 @@
//go:build !windows && !linux && !darwin && !aix && !ppc64 && !dragonfly && !js && !plan9 && !solaris && !openbsd && !freebsd && !netbsd && !illumos && !wasm && !wasip1 && !android
// +build !windows,!linux,!darwin,!aix,!ppc64,!dragonfly,!js,!plan9,!solaris,!openbsd,!freebsd,!netbsd,!illumos,!wasm,!wasip1,!android
package cmd
import (
"errors"
"log/slog"
"runtime"
)
// 默认实现,用于不支持的平台
func initWgTunFunctions() {
// 初始化平台特定的函数
configureLinuxWgTunIP = func(tunName, cidr string) error {
return errors.New("Linux TUN配置在此平台上不可用")
}
configureDarwinWgTunIP = func(tunName, cidr string) error {
return errors.New("macOS TUN配置在此平台上不可用")
}
configureWindowsWgTunIP = func(tunName, cidr string) error {
return errors.New("Windows TUN配置在此平台上不可用")
}
}
func init() {
initWgTunFunctions()
// 记录不支持的平台警告
slog.Warn("当前平台不完全支持WireGuard TUN接口", "platform", runtime.GOOS)
}

View File

@@ -0,0 +1,40 @@
//go:build linux
// +build linux
package cmd
import (
"log/slog"
"net"
"os/exec"
)
// 初始化Linux平台的TUN配置函数
func init() {
configureLinuxWgTunIP = func(tunName, cidr string) error {
slog.Info("配置Linux WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 使用ip命令配置TUN设备IP地址
cmd := exec.Command("ip", "addr", "add", cidr, "dev", tunName)
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
// 启用设备
cmd = exec.Command("ip", "link", "set", "dev", tunName, "up")
if err := cmd.Run(); err != nil {
slog.Error("启用设备失败", "error", err)
return err
}
slog.Info("已配置Linux WireGuard TUN设备IP", "device", tunName, "ip", ip, "network", ipNet)
return nil
}
}

View File

@@ -0,0 +1,49 @@
//go:build solaris || illumos
// +build solaris illumos
package cmd
import (
"log/slog"
"net"
"os/exec"
)
// 初始化Solaris/Illumos平台的TUN配置函数
func init() {
// 为Linux配置实现提供Solaris/Illumos平台的配置函数
configureLinuxWgTunIP = func(tunName, cidr string) error {
slog.Info("配置Solaris/Illumos系统WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 在Solaris/Illumos系统上可以使用ifconfig来配置TUN设备
cmd := exec.Command("ifconfig", tunName, "inet", ip.String(), "netmask", formatNetmask(ipNet.Mask), "up")
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
slog.Info("已配置Solaris/Illumos系统WireGuard TUN设备IP", "device", tunName, "ip", ip, "network", ipNet)
return nil
}
// Darwin配置函数也用于Solaris/Illumos系统
configureDarwinWgTunIP = configureLinuxWgTunIP
// Windows配置函数在Solaris/Illumos系统上不可用
configureWindowsWgTunIP = func(tunName, cidr string) error {
return configureLinuxWgTunIP(tunName, cidr)
}
slog.Info("已初始化Solaris/Illumos系统WireGuard TUN配置函数")
}
// formatNetmask 将IP掩码格式化为Solaris/Illumos所需的格式
func formatNetmask(mask net.IPMask) string {
return net.IP(mask).String()
}

View File

@@ -0,0 +1,56 @@
//go:build windows
// +build windows
package cmd
import (
"fmt"
"log/slog"
"net"
"os/exec"
"strings"
)
// 初始化Windows平台的TUN配置函数
func init() {
configureWindowsWgTunIP = func(tunName, cidr string) error {
slog.Info("配置Windows WireGuard TUN设备IP", "device", tunName, "cidr", cidr)
// 解析IP和子网掩码
ip, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return err
}
// 在Windows上需要使用netsh命令配置网络接口
// 首先,需要找到接口的索引或名称
// 我们假设tunName已经包含了接口名称或索引
// 构建子网掩码字符串
ones, _ := ipNet.Mask.Size()
// 在Windows中接口名称可能包含空格需要用引号处理
interfaceName := tunName
if !strings.HasPrefix(interfaceName, "\"") {
interfaceName = "\"" + interfaceName + "\""
}
// 使用netsh命令设置IP地址
cmd := exec.Command("netsh", "interface", "ip", "set", "address",
"name="+interfaceName, "source=static", "addr="+ip.String(),
"mask="+convertMaskToCIDR(ones))
if err := cmd.Run(); err != nil {
slog.Error("配置IP地址失败", "error", err)
return err
}
slog.Info("已配置Windows WireGuard TUN设备IP", "device", tunName, "ip", ip, "prefix_length", ones)
return nil
}
}
// convertMaskToCIDR 将子网掩码位数转换为CIDR表示法
func convertMaskToCIDR(ones int) string {
return fmt.Sprintf("%d", ones)
}

View File

@@ -1,3 +1,6 @@
//go:build !android || !386
// +build !android !386
package main
import "github.com/pojntfx/weron/cmd/weron/cmd"

View File

@@ -1,3 +1,6 @@
//go:build !android || !386
// +build !android !386
package main
import (
@@ -17,21 +20,21 @@ import (
)
var (
errMissingCommunity = errors.New("missing community")
errMissingPassword = errors.New("missing password")
errMissingKey = errors.New("missing key")
errMissingCommunity = errors.New("缺少社区ID")
errMissingPassword = errors.New("缺少密码")
errMissingKey = errors.New("缺少加密密钥")
)
func main() {
verboseFlag := flag.Int("verbose", 5, "Verbosity level (0 is disabled, default is info, 7 is trace)")
raddrFlag := flag.String("raddr", "wss://weron.up.railway.app/", "Remote address")
timeoutFlag := flag.Duration("timeout", time.Second*10, "Time to wait for connections")
communityFlag := flag.String("community", "", "ID of community to join")
passwordFlag := flag.String("password", "", "Password for community")
keyFlag := flag.String("key", "", "Encryption key for community")
iceFlag := flag.String("ice", "stun:stun.l.google.com:19302", "Comma-separated list of STUN servers (in format stun:host:port) and TURN servers to use (in format username:credential@turn:host:port) (i.e. username:credential@turn:global.turn.twilio.com:3478?transport=tcp)")
forceRelayFlag := flag.Bool("force-relay", false, "Force usage of TURN servers")
pauseFlag := flag.Duration("pause", time.Second*1, "Time to wait before sending next message")
verboseFlag := flag.Int("verbose", 5, "详细级别 (0表示禁用, 默认为info级别, 7表示trace级别)")
raddrFlag := flag.String("raddr", "wss://weron.up.railway.app/", "远程地址")
timeoutFlag := flag.Duration("timeout", time.Second*10, "连接等待时间")
communityFlag := flag.String("community", "", "要加入的社区ID")
passwordFlag := flag.String("password", "", "社区密码")
keyFlag := flag.String("key", "", "社区加密密钥")
iceFlag := flag.String("ice", "stun:stun.l.google.com:19302", "STUN服务器格式为stun:host:port和TURN服务器格式为username:credential@turn:host:port)的逗号分隔列表(例如:username:credential@turn:global.turn.twilio.com:3478?transport=tcp")
forceRelayFlag := flag.Bool("force-relay", false, "强制使用TURN服务器")
pauseFlag := flag.Duration("pause", time.Second*1, "发送下一条消息前的等待时间")
flag.Parse()
@@ -66,7 +69,7 @@ func main() {
panic(errMissingKey)
}
log.Println("Connecting to signaler with address", *raddrFlag)
log.Println("正在连接到信令服务器,地址为", *raddrFlag)
u, err := url.Parse(*raddrFlag)
if err != nil {
@@ -87,7 +90,7 @@ func main() {
Timeout: *timeoutFlag,
ForceRelay: *forceRelayFlag,
OnSignalerReconnect: func() {
log.Println("Reconnecting to signaler with address", *raddrFlag)
log.Println("正在重新连接到信令服务器,地址为", *raddrFlag)
},
},
ctx,
@@ -114,14 +117,14 @@ func main() {
case rid := <-ids:
id = rid
log.Println("Connected to signaler with address", *raddrFlag, "and ID", rid)
log.Println("已连接到信令服务器,地址为", *raddrFlag, "ID", rid)
case peer := <-adapter.Accept():
go func() {
defer func() {
log.Println("Disconnected from peer with ID", peer.PeerID, "and channel", peer.ChannelID)
log.Println("已断开与对等端的连接,对等端ID", peer.PeerID, ",通道为", peer.ChannelID)
}()
log.Println("Connected to peer with ID", peer.PeerID, "and channel", peer.ChannelID)
log.Println("已连接到对等端,对等端ID", peer.PeerID, ",通道为", peer.ChannelID)
go func() {
ticker := time.NewTicker(*pauseFlag)
@@ -133,7 +136,7 @@ func main() {
return
case <-ticker.C:
if _, err := peer.Conn.Write([]byte(fmt.Sprintf("Hello from %v! It is currently %v local time.\n", id, time.Now().Local()))); err != nil {
if _, err := peer.Conn.Write([]byte(fmt.Sprintf("来自 %v 的问候!当前本地时间是 %v。\n", id, time.Now().Local()))); err != nil {
return
}
}
@@ -143,7 +146,7 @@ func main() {
reader := bufio.NewScanner(peer.Conn)
for reader.Scan() {
fmt.Printf("Got message from peer %v: %v\n", peer.PeerID, reader.Text())
fmt.Printf("收到来自对等端 %v 的消息: %v\n", peer.PeerID, reader.Text())
}
}()
}

2
go.mod
View File

@@ -24,6 +24,7 @@ require (
github.com/volatiletech/strmangle v0.0.8
golang.org/x/crypto v0.36.0
golang.org/x/sync v0.12.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
)
require (
@@ -70,6 +71,7 @@ require (
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

10
go.sum
View File

@@ -200,6 +200,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -832,6 +834,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -898,6 +902,10 @@ golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1092,6 +1100,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=