/* 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
}