aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/service/tunneltracker.go
blob: 2545930de857856b39cfafe0751f32e2a32a40cf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
 */

package service

import (
	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/svc/mgr"
	"golang.zx2c4.com/wireguard/windows/conf"
	"runtime"
	"unsafe"
)

//sys notifyServiceStatusChange(service windows.Handle, notifyMask uint32, notifyBuffer uintptr) (err error) [failretval!=0] = advapi32.NotifyServiceStatusChangeW
//sys sleepEx(milliseconds uint32, alertable bool) (ret uint32, err error) = kernel32.SleepEx

const (
	serviceNotify_CREATED          uint32 = 0x00000080
	serviceNotify_CONTINUE_PENDING        = 0x00000010
	serviceNotify_DELETE_PENDING          = 0x00000200
	serviceNotify_DELETED                 = 0x00000100
	serviceNotify_PAUSE_PENDING           = 0x00000020
	serviceNotify_PAUSED                  = 0x00000040
	serviceNotify_RUNNING                 = 0x00000008
	serviceNotify_START_PENDING           = 0x00000002
	serviceNotify_STOP_PENDING            = 0x00000004
	serviceNotify_STOPPED                 = 0x00000001
)
const serviceNotify_STATUS_CHANGE uint32 = 2
const errorServiceMARKED_FOR_DELETE uint32 = 1072

type serviceNotify struct {
	version                 uint32
	notifyCallback          uintptr
	context                 uintptr
	notificationStatus      uint32
	serviceType             uint32
	currentState            uint32
	controlsAccepted        uint32
	win32ExitCode           uint32
	serviceSpecificExitCode uint32
	checkPoint              uint32
	waitHint                uint32
	processId               uint32
	serviceFlags            uint32
	notificationTriggered   uint32
	serviceNames            *uint16
}

func serviceTrackerCallback(notifier *serviceNotify) uintptr {
	return 0
}

var serviceTrackerCallbackPtr uintptr

func init() {
	serviceTrackerCallbackPtr = windows.NewCallback(serviceTrackerCallback)
}

func trackExistingTunnels() error {
	m, err := serviceManager()
	if err != nil {
		return err
	}
	names, err := conf.ListConfigNames()
	if err != nil {
		return err
	}
	for _, name := range names {
		serviceName := "WireGuard Tunnel: " + name
		service, err := m.OpenService(serviceName)
		if err != nil {
			continue
		}
		go trackTunnelService(name, service)
	}
	return nil
}

func trackTunnelService(tunnelName string, svc *mgr.Service) {
	runtime.LockOSThread()
	const serviceNotifications = serviceNotify_RUNNING | serviceNotify_START_PENDING | serviceNotify_STOP_PENDING | serviceNotify_STOPPED | serviceNotify_DELETE_PENDING
	notifier := &serviceNotify{
		version:        serviceNotify_STATUS_CHANGE,
		notifyCallback: serviceTrackerCallbackPtr,
	}
	defer svc.Close()
	for {
		notifier.context = 0
		err := notifyServiceStatusChange(svc.Handle, serviceNotifications, uintptr(unsafe.Pointer(notifier)))
		if err != nil {
			return
		}
		sleepEx(windows.INFINITE, true)
		if notifier.notificationStatus != 0 {
			return
		}
		state := TunnelUnknown
		if notifier.notificationTriggered&serviceNotify_DELETE_PENDING != 0 {
			state = TunnelDeleting
		} else if notifier.notificationTriggered&serviceNotify_STOPPED != 0 {
			state = TunnelStopped
		} else if notifier.notificationTriggered&serviceNotify_STOP_PENDING != 0 {
			state = TunnelStopping
		} else if notifier.notificationTriggered&serviceNotify_RUNNING != 0 {
			state = TunnelStarted
		} else if notifier.notificationTriggered&serviceNotify_START_PENDING != 0 {
			state = TunnelStarting
		}
		IPCServerNotifyTunnelChange(tunnelName, state)
		if state == TunnelDeleting {
			return
		}
	}
}