Release 1.8.0: auto-update with restart and AmneziaWG 2.0 support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package awg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/amnezia-vpn/amneziawg-go/conn"
|
||||
"github.com/amnezia-vpn/amneziawg-go/device"
|
||||
"github.com/amnezia-vpn/amneziawg-go/tun/netstack"
|
||||
|
||||
"vpnclient/internal/config"
|
||||
)
|
||||
|
||||
// Engine runs AmneziaWG 2.0 in userspace (gvisor netstack) and exposes local SOCKS/HTTP.
|
||||
type Engine struct {
|
||||
mu sync.Mutex
|
||||
dev *device.Device
|
||||
tnet *netstack.Net
|
||||
proxies *proxyServer
|
||||
profile config.Profile
|
||||
stderr io.Writer
|
||||
running bool
|
||||
}
|
||||
|
||||
func New(stderr io.Writer) *Engine {
|
||||
if stderr == nil {
|
||||
stderr = os.Stderr
|
||||
}
|
||||
return &Engine{stderr: stderr}
|
||||
}
|
||||
|
||||
func (e *Engine) Protocol() config.Protocol { return config.ProtocolAWG }
|
||||
|
||||
func (e *Engine) Start(ctx context.Context, profile config.Profile, _ string) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if e.running {
|
||||
return fmt.Errorf("awg: already running")
|
||||
}
|
||||
|
||||
cfg, err := Parse(profile.Proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipc, err := cfg.ToIPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addrs, err := cfg.LocalAddrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tunDev, tnet, err := netstack.CreateNetTUN(addrs, cfg.DNSAddrs(), cfg.EffectiveMTU())
|
||||
if err != nil {
|
||||
return fmt.Errorf("awg netstack: %w", err)
|
||||
}
|
||||
logger := &device.Logger{
|
||||
Verbosef: func(format string, args ...any) {},
|
||||
Errorf: func(format string, args ...any) {
|
||||
fmt.Fprintf(e.stderr, "awg: "+format+"\n", args...)
|
||||
},
|
||||
}
|
||||
dev := device.NewDevice(tunDev, conn.NewDefaultBind(), logger)
|
||||
if err := dev.IpcSet(ipc); err != nil {
|
||||
dev.Close()
|
||||
return fmt.Errorf("awg configure: %w", err)
|
||||
}
|
||||
if err := dev.Up(); err != nil {
|
||||
dev.Close()
|
||||
return fmt.Errorf("awg up: %w", err)
|
||||
}
|
||||
|
||||
socksHP, _ := profile.SOCKSListenHostPort()
|
||||
httpHP, _ := profile.HTTPListenHostPort()
|
||||
if socksHP == "" {
|
||||
socksHP = "127.0.0.1:1080"
|
||||
}
|
||||
if httpHP == "" {
|
||||
httpHP = "127.0.0.1:1081"
|
||||
}
|
||||
|
||||
proxies, err := startProxies(socksHP, httpHP, tnet.DialContext)
|
||||
if err != nil {
|
||||
dev.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
e.dev = dev
|
||||
e.tnet = tnet
|
||||
e.proxies = proxies
|
||||
e.profile = profile
|
||||
e.running = true
|
||||
|
||||
// Brief handshake grace; don't fail hard if peer is slow.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
_ = e.stopLocked()
|
||||
return ctx.Err()
|
||||
case <-time.After(400 * time.Millisecond):
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) Stop() error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.stopLocked()
|
||||
}
|
||||
|
||||
func (e *Engine) stopLocked() error {
|
||||
if !e.running {
|
||||
return nil
|
||||
}
|
||||
if e.proxies != nil {
|
||||
_ = e.proxies.Close()
|
||||
e.proxies = nil
|
||||
}
|
||||
if e.dev != nil {
|
||||
e.dev.Close()
|
||||
e.dev = nil
|
||||
}
|
||||
e.tnet = nil
|
||||
e.running = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) Running() bool {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.running
|
||||
}
|
||||
|
||||
func (e *Engine) LocalHTTPProxy() (string, bool) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if !e.running {
|
||||
return "", false
|
||||
}
|
||||
if e.proxies != nil && e.proxies.httpAddr != "" {
|
||||
return e.proxies.httpAddr, true
|
||||
}
|
||||
return e.profile.HTTPListenHostPort()
|
||||
}
|
||||
|
||||
func (e *Engine) LocalSOCKSProxy() (string, bool) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if !e.running {
|
||||
return "", false
|
||||
}
|
||||
if e.proxies != nil && e.proxies.socksAddr != "" {
|
||||
return e.proxies.socksAddr, true
|
||||
}
|
||||
return e.profile.SOCKSListenHostPort()
|
||||
}
|
||||
|
||||
// EnsureBinary is a no-op: AWG is embedded via amneziawg-go.
|
||||
func EnsureBinary(_ string) (string, error) {
|
||||
return "embedded-amneziawg-go", nil
|
||||
}
|
||||
|
||||
// ResolveBinary reports the embedded core.
|
||||
func ResolveBinary(_ string) (string, error) {
|
||||
return "embedded-amneziawg-go", nil
|
||||
}
|
||||
Reference in New Issue
Block a user