Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
3.3 KB go
/* SPDX-License-Identifier: MIT
 *
 * Phobos
 */

package tunnel

import (
	"context"
	"fmt"
	"log"
	"net"
	"time"

	"golang.org/x/sys/windows"
	"golang.zx2c4.com/wireguard/windows/conf"
	"golang.zx2c4.com/wireguard/windows/phobos"
	"golang.zx2c4.com/wireguard/windows/services"
	"golang.zx2c4.com/wireguard/windows/tun2socks"
	"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
	"golang.zx2c4.com/wireguard/windows/wintun"
)

const socks5RingCapacity = 0x400000

type socks5Dialer struct {
	client *phobos.Socks5Client
}

func (d socks5Dialer) DialTCP(ctx context.Context, host string, port uint16) (net.Conn, error) {
	return d.client.DialTCP(ctx, host, port)
}

func (d socks5Dialer) DialUDP(ctx context.Context) (tun2socks.PacketSession, error) {
	return d.client.DialUDP(ctx)
}

type socks5Tunnel struct {
	adapter *wintun.Adapter
	session *wintun.Session
	client  *phobos.Socks5Client
	stack   *tun2socks.Tunnel
	binder  stickyBinder
}

func createSocks5Adapter(config *conf.Config) (*wintun.Adapter, error) {
	var adapter *wintun.Adapter
	var err error
	for i := range 15 {
		if i > 0 {
			time.Sleep(time.Second)
			log.Printf("Retrying adapter creation after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
		}
		adapter, err = wintun.CreateAdapter(config.Name, "Phobos", deterministicGUID(config))
		if err == nil || !services.StartedAtBoot() {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("Error creating adapter: %w", err)
	}
	if version, err := wintun.RunningVersion(); err != nil {
		log.Printf("Warning: unable to determine Wintun version: %v", err)
	} else {
		log.Printf("Using Wintun/%d.%d", (version>>16)&0xffff, version&0xffff)
	}
	return adapter, nil
}

func startSocks5Tunnel(config *conf.Config, adapter *wintun.Adapter, ourLUID winipcfg.LUID) (*socks5Tunnel, error) {
	settings := config.Obfuscation
	target, err := resolvedEndpoint(&settings.Target)
	if err != nil {
		return nil, err
	}

	t := &socks5Tunnel{adapter: adapter, binder: stickyBinder{ourLUID: ourLUID}}
	t.client = phobos.NewSocks5Client(phobos.Socks5Config{
		Target:     target,
		Key:        []byte(settings.Key),
		Masking:    settings.Masking,
		Media:      settings.MediaParams(),
		Login:      settings.Login,
		Password:   settings.Password,
		ListenPort: settings.SourceListenPort,
		Control:    t.binder.control,
		Logf:       log.Printf,
	})
	if err := t.client.Start(); err != nil {
		return nil, err
	}

	t.session, err = adapter.StartSession(socks5RingCapacity)
	if err != nil {
		t.stop()
		return nil, fmt.Errorf("Error starting adapter session: %w", err)
	}

	t.stack, err = tun2socks.Start(tun2socks.Config{
		Session: t.session,
		MTU:     uint32(config.Interface.MTU),
		Dialer:  socks5Dialer{client: t.client},
		Logf:    log.Printf,
	})
	if err != nil {
		t.stop()
		return nil, err
	}

	log.Printf("SOCKS5 tunnel up: %v (masking %v)", target, settings.Masking)
	return t, nil
}

func (t *socks5Tunnel) stop() {
	if t == nil {
		return
	}
	t.binder.stopWatching()
	if t.stack != nil {
		t.stack.Stop()
		t.stack = nil
	}
	if t.session != nil {
		t.session.End()
		t.session = nil
	}
	if t.client != nil {
		t.client.Stop()
		t.client = nil
	}
}

func (t *socks5Tunnel) watchDefaultRoutes(ourLUID winipcfg.LUID) error {
	if t == nil {
		return nil
	}
	return t.binder.watchDefaultRoutes(ourLUID)
}

func (t *socks5Tunnel) Reconfigure(*conf.Config) error {
	return nil
}