mirror of
https://git.zx2c4.com/wireguard-windows
synced 2024-11-10 16:59:18 +00:00
6ed37f30f5
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
243 lines
5.3 KiB
Go
243 lines
5.3 KiB
Go
/* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
|
|
*/
|
|
|
|
package manager
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/svc"
|
|
"golang.org/x/sys/windows/svc/mgr"
|
|
|
|
"golang.zx2c4.com/wireguard/windows/conf"
|
|
)
|
|
|
|
var cachedServiceManager *mgr.Mgr
|
|
|
|
func serviceManager() (*mgr.Mgr, error) {
|
|
if cachedServiceManager != nil {
|
|
return cachedServiceManager, nil
|
|
}
|
|
m, err := mgr.Connect()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cachedServiceManager = m
|
|
return cachedServiceManager, nil
|
|
}
|
|
|
|
var ErrManagerAlreadyRunning = errors.New("Manager already installed and running")
|
|
|
|
func InstallManager() error {
|
|
m, err := serviceManager()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
path, err := os.Executable()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
// TODO: Do we want to bail if executable isn't being run from the right location?
|
|
|
|
serviceName := "WireGuardManager"
|
|
service, err := m.OpenService(serviceName)
|
|
if err == nil {
|
|
status, err := service.Query()
|
|
if err != nil {
|
|
service.Close()
|
|
return err
|
|
}
|
|
if status.State != svc.Stopped {
|
|
service.Close()
|
|
if status.State == svc.StartPending {
|
|
// We were *just* started by something else, so return success here, assuming the other program
|
|
// starting this does the right thing. This can happen when, e.g., the updater relaunches the
|
|
// manager service and then invokes wireguard.exe to raise the UI.
|
|
return nil
|
|
}
|
|
return ErrManagerAlreadyRunning
|
|
}
|
|
err = service.Delete()
|
|
service.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
service, err = m.OpenService(serviceName)
|
|
if err != nil {
|
|
break
|
|
}
|
|
service.Close()
|
|
time.Sleep(time.Second / 3)
|
|
}
|
|
}
|
|
|
|
config := mgr.Config{
|
|
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
|
|
StartType: mgr.StartAutomatic,
|
|
ErrorControl: mgr.ErrorNormal,
|
|
DisplayName: "WireGuard Manager",
|
|
}
|
|
|
|
service, err = m.CreateService(serviceName, path, config, "/managerservice")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service.Start()
|
|
return service.Close()
|
|
}
|
|
|
|
func UninstallManager() error {
|
|
m, err := serviceManager()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serviceName := "WireGuardManager"
|
|
service, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service.Control(svc.Stop)
|
|
err = service.Delete()
|
|
err2 := service.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return err2
|
|
}
|
|
|
|
func InstallTunnel(configPath string) error {
|
|
m, err := serviceManager()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
path, err := os.Executable()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
name, err := conf.NameFromPath(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
serviceName, err := conf.ServiceNameOfTunnel(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service, err := m.OpenService(serviceName)
|
|
if err == nil {
|
|
status, err := service.Query()
|
|
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
|
|
service.Close()
|
|
return err
|
|
}
|
|
if status.State != svc.Stopped && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
|
|
service.Close()
|
|
return errors.New("Tunnel already installed and running")
|
|
}
|
|
err = service.Delete()
|
|
service.Close()
|
|
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
|
|
return err
|
|
}
|
|
for {
|
|
service, err = m.OpenService(serviceName)
|
|
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
|
|
break
|
|
}
|
|
service.Close()
|
|
time.Sleep(time.Second / 3)
|
|
}
|
|
}
|
|
|
|
config := mgr.Config{
|
|
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
|
|
StartType: mgr.StartAutomatic,
|
|
ErrorControl: mgr.ErrorNormal,
|
|
Dependencies: []string{"Nsi", "TcpIp"},
|
|
DisplayName: "WireGuard Tunnel: " + name,
|
|
SidType: windows.SERVICE_SID_TYPE_UNRESTRICTED,
|
|
}
|
|
service, err = m.CreateService(serviceName, path, config, "/tunnelservice", configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = service.Start()
|
|
go trackTunnelService(name, service) // Pass off reference to handle.
|
|
return err
|
|
}
|
|
|
|
func UninstallTunnel(name string) error {
|
|
m, err := serviceManager()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
serviceName, err := conf.ServiceNameOfTunnel(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service, err := m.OpenService(serviceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
service.Control(svc.Stop)
|
|
err = service.Delete()
|
|
err2 := service.Close()
|
|
if err != nil && err != windows.ERROR_SERVICE_MARKED_FOR_DELETE {
|
|
return err
|
|
}
|
|
return err2
|
|
}
|
|
|
|
func changeTunnelServiceConfigFilePath(name, oldPath, newPath string) {
|
|
var err error
|
|
defer func() {
|
|
if err != nil {
|
|
log.Printf("Unable to change tunnel service command line argument from %#q to %#q: %v", oldPath, newPath, err)
|
|
}
|
|
}()
|
|
m, err := serviceManager()
|
|
if err != nil {
|
|
return
|
|
}
|
|
serviceName, err := conf.ServiceNameOfTunnel(name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
service, err := m.OpenService(serviceName)
|
|
if err == windows.ERROR_SERVICE_DOES_NOT_EXIST {
|
|
err = nil
|
|
return
|
|
} else if err != nil {
|
|
return
|
|
}
|
|
defer service.Close()
|
|
config, err := service.Config()
|
|
if err != nil {
|
|
return
|
|
}
|
|
exePath, err := os.Executable()
|
|
if err != nil {
|
|
return
|
|
}
|
|
args, err := windows.DecomposeCommandLine(config.BinaryPathName)
|
|
if err != nil || len(args) != 3 ||
|
|
!strings.EqualFold(args[0], exePath) || args[1] != "/tunnelservice" || !strings.EqualFold(args[2], oldPath) {
|
|
err = nil
|
|
return
|
|
}
|
|
args[2] = newPath
|
|
config.BinaryPathName = windows.ComposeCommandLine(args)
|
|
err = service.UpdateConfig(config)
|
|
}
|