Skip to content

Commit

Permalink
Add interface wrappers and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
godstanis committed Jan 2, 2025
1 parent 88c1180 commit b4e45d4
Show file tree
Hide file tree
Showing 9 changed files with 802 additions and 91 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
49 changes: 25 additions & 24 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,61 @@ module github.com/goxray/tun
go 1.23.3

require (
github.com/goxray/core v0.0.0-20241217043310-fb75955c8e9b
github.com/jackpal/gateway v1.0.15
github.com/lilendian0x00/xray-knife v1.6.14-0.20241211194505-363c66ecace3
github.com/goxray/core v0.0.1
github.com/jackpal/gateway v1.0.16
github.com/lilendian0x00/xray-knife/v2 v2.14.21
github.com/stretchr/testify v1.10.0
github.com/xtls/xray-core v1.8.24
go.uber.org/mock v0.5.0
)

require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/cloudflare/circl v1.4.0 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/eycorsican/go-tun2socks v1.16.11 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sagernet/sing v0.4.1 // indirect
github.com/sagernet/sing v0.5.1 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/vishvananda/netlink v1.3.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect
go.uber.org/mock v0.4.0 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.22.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.28.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/grpc v1.66.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241230172942-26aa7a208def // indirect
google.golang.org/grpc v1.69.2 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
Expand Down
118 changes: 68 additions & 50 deletions go.sum

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func main() {
log.Fatal(err)
}

slog.Info("Connected to VPN server")
<-sigterm
slog.Info("Received term signal, disconnecting...")
if err = vpn.Disconnect(context.Background()); err != nil {
Expand Down
52 changes: 35 additions & 17 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (

"github.com/goxray/core/network/route"
"github.com/goxray/core/network/tun"
tun2socks "github.com/goxray/core/pipe2socks"
"github.com/goxray/core/pipe2socks"

"github.com/jackpal/gateway"
"github.com/lilendian0x00/xray-knife/xray"
"github.com/lilendian0x00/xray-knife/v2/xray"
xapplog "github.com/xtls/xray-core/app/log"
xcommlog "github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/core"
Expand Down Expand Up @@ -96,9 +96,11 @@ func (c *Config) apply(new *Config) {
type Client struct {
cfg Config

xInst *core.Instance
xInst runnable
xCfg *xray.GeneralConfig
tunnel io.ReadWriteCloser
pipe pipe
routes ipTable

tunnelStopped chan error
stopTunnel func()
Expand All @@ -122,6 +124,16 @@ func NewClient() (*Client, error) {
return nil, fmt.Errorf("discover gateway: %w", err)
}

p, err := pipe2socks.NewPipe(pipe2socks.DefaultOpts)
if err != nil {
return nil, fmt.Errorf("tun2socks new pipe: %w", err)
}

r, err := route.New()
if err != nil {
return nil, fmt.Errorf("route new: %w", err)
}

return &Client{
cfg: Config{
GatewayIP: &gatewayIP,
Expand All @@ -131,6 +143,8 @@ func NewClient() (*Client, error) {
Logger: slog.New(slog.NewTextHandler(os.Stdout, nil)),
},
tunnelStopped: make(chan error),
pipe: p,
routes: r,
}, nil
}

Expand Down Expand Up @@ -174,39 +188,39 @@ func (c *Client) Connect(link string) error {
if err != nil {
c.cfg.Logger.Error("xray core creation failed", "err", err, "xray_config", c.xCfg)

return fmt.Errorf("create xray core instance: %v", err)
return fmt.Errorf("create xray core instance: %w", err)
}
c.cfg.Logger.Debug("xray core instance created", "xray_config", c.xCfg)

c.cfg.Logger.Debug("starting xray core instance")
if err = c.xInst.Start(); err != nil {
c.cfg.Logger.Error("xray core instance startup failed", "err", err)

return fmt.Errorf("start xray core instance: %v", err)
return fmt.Errorf("start xray core instance: %w", err)
}
time.Sleep(100 * time.Millisecond) // Sometimes XRay instance should have a bit more time to set up.
c.cfg.Logger.Debug("xray core instance started")

c.cfg.Logger.Debug("Setting up TUN device")
// Create TUN and route all traffic to it.
c.tunnel, err = setupTunnel(c.cfg.TUNAddress, c.cfg.TUNAddress.IP, c.cfg.RoutesToTUN)
c.tunnel, err = c.setupTunnel()
if err != nil {
c.cfg.Logger.Error("TUN creation failed", "err", err)

return fmt.Errorf("setup TUN device: %v", err)
return fmt.Errorf("setup TUN device: %w", err)
}
c.tunnel = newReaderMetrics(c.tunnel)
c.cfg.Logger.Debug("TUN device created")

c.cfg.Logger.Debug("adding routes for TUN device")
// Set XRay remote address to be routed through the default gateway, so that we don't get a loop.
_ = route.Delete(c.xrayToGatewayRoute()) // In case previous run failed.
_ = c.routes.Delete(c.xrayToGatewayRoute()) // In case previous run failed.
c.cfg.Logger.Debug("deleted dangling routes")
err = route.Add(c.xrayToGatewayRoute())
err = c.routes.Add(c.xrayToGatewayRoute())
if err != nil {
c.cfg.Logger.Error("routing xray server IP to default route failed", "err", err, "route", c.xrayToGatewayRoute())

return fmt.Errorf("add xray server route exception: %v", err)
return fmt.Errorf("add xray server route exception: %w", err)
}
c.cfg.Logger.Debug("routing xray server IP to default route")

Expand All @@ -216,7 +230,7 @@ func (c *Client) Connect(link string) error {
ctx, c.stopTunnel = context.WithCancel(context.Background())
go func() {
wg.Done()
c.tunnelStopped <- tun2socks.Copy(ctx, c.tunnel, c.cfg.InboundProxy.String(), nil)
c.tunnelStopped <- c.pipe.Copy(ctx, c.tunnel, c.cfg.InboundProxy.String())
c.cfg.Logger.Debug("tunnel pipe closed", "err", err)
}()
wg.Wait()
Expand All @@ -230,8 +244,12 @@ func (c *Client) Connect(link string) error {
// It will block till all resources are done processing or
// context is cancelled (method also enforces timeout of disconnectTimeout)
func (c *Client) Disconnect(ctx context.Context) error {
if c.stopTunnel == nil {
return nil // not connected
}

c.stopTunnel()
err := errors.Join(c.xInst.Close(), c.tunnel.Close(), route.Delete(c.xrayToGatewayRoute()))
err := errors.Join(c.xInst.Close(), c.tunnel.Close(), c.routes.Delete(c.xrayToGatewayRoute()))

// Waiting till the tunnel actually done with processing connections.
ctx, cancel := context.WithTimeout(ctx, disconnectTimeout)
Expand Down Expand Up @@ -283,7 +301,7 @@ func (c *Client) xrayToGatewayRoute() route.Opts {
func (c *Client) createXrayProxy(link string) (*core.Instance, *xray.GeneralConfig, error) {
protocol, err := xray.ParseXrayConfig(link)
if err != nil {
return nil, nil, fmt.Errorf("parse xray config link: %w", err)
return nil, nil, fmt.Errorf("parse config link: %w", err)
}

// Make the inbound for local proxy.
Expand All @@ -302,7 +320,7 @@ func (c *Client) createXrayProxy(link string) (*core.Instance, *xray.GeneralConf

inst, err := svc.MakeXrayInstance(protocol)
if err != nil {
return nil, nil, fmt.Errorf("make xray instance: %w", err)
return nil, nil, fmt.Errorf("make instance: %w", err)
}

cfg := protocol.ConvertToGeneralConfig()
Expand All @@ -328,17 +346,17 @@ func xRayLogLevel(h slog.Handler) xcommlog.Severity {
}

// setupTunnel creates new TUN interface in the system and routes all traffic to it.
func setupTunnel(l *net.IPNet, gw net.IP, rerouteToTun []*route.Addr) (*tun.Interface, error) {
func (c *Client) setupTunnel() (*tun.Interface, error) {
ifc, err := tun.New("", 1500)
if err != nil {
return nil, fmt.Errorf("create tun: %w", err)
}

if err = ifc.Up(l, gw); err != nil {
if err = ifc.Up(c.cfg.TUNAddress, c.cfg.TUNAddress.IP); err != nil {
return nil, fmt.Errorf("setup interface: %w", err)
}

if err = route.Add(route.Opts{IfName: ifc.Name(), Routes: rerouteToTun}); err != nil {
if err = c.routes.Add(route.Opts{IfName: ifc.Name(), Routes: c.cfg.RoutesToTUN}); err != nil {
return nil, fmt.Errorf("add route: %w", err)
}

Expand Down
Loading

0 comments on commit b4e45d4

Please sign in to comment.