aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/service/ipc_server.go
blob: 4388bb00bad473d6734cf3fd157b236ac9aae557 (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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* SPDX-License-Identifier: MIT
 *
 * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
 */

package service

import (
	"bytes"
	"encoding/gob"
	"errors"
	"golang.zx2c4.com/wireguard/windows/conf"
	"net/rpc"
	"os"
	"sync"
	"sync/atomic"
	"time"
)

var managerServices = make(map[*ManagerService]bool)
var managerServicesLock sync.RWMutex
var haveQuit uint32
var quitManagersChan = make(chan struct{}, 1)

type ManagerService struct {
	events *os.File
}

func (s *ManagerService) StoredConfig(tunnelName string, config *conf.Config) error {
	c, err := conf.LoadFromName(tunnelName)
	if err != nil {
		return err
	}
	*config = *c
	return nil
}

func (s *ManagerService) RuntimeConfig(tunnelName string, config *conf.Config) error {
	//TODO

	return nil
}

func (s *ManagerService) Start(tunnelName string, state *TunnelState) error {
	c, err := conf.LoadFromName(tunnelName)
	if err != nil {
		return err
	}
	path, err := c.Path()
	if err != nil {
		return err
	}
	return InstallTunnel(path)
	//TODO: write out *state
}

func (s *ManagerService) Stop(tunnelName string, state *TunnelState) error {
	return UninstallTunnel(tunnelName)
	//TODO: This function should do nothing if the tunnel is already stopped
	//TODO: write out *state
}

func (s *ManagerService) Delete(tunnelName string, state *TunnelState) error {
	err := s.Stop(tunnelName, state)
	if err != nil {
		return err
	}
	//TODO: wait for stopped somehow
	if *state != TunnelStopped {
		return errors.New("Unable to stop tunnel before deleting")
	}
	return conf.DeleteName(tunnelName)
}

func (s *ManagerService) State(tunnelName string, state *TunnelState) error {
	//TODO

	return nil
}

func (s *ManagerService) Create(tunnelConfig conf.Config, tunnel *Tunnel) error {
	err := tunnelConfig.Save()
	if err != nil {
		return err
	}
	*tunnel = Tunnel{tunnelConfig.Name}
	return nil
	//TODO: handle already existing situation
	//TODO: handle already running and existing situation
}

func (s *ManagerService) Tunnels(unused uintptr, tunnels *[]Tunnel) error {
	names, err := conf.ListConfigNames()
	if err != nil {
		return err
	}
	*tunnels = make([]Tunnel, len(names))
	for i := 0; i < len(*tunnels); i++ {
		(*tunnels)[i].Name = names[i]
	}
	return nil
	//TODO: account for running ones that aren't in the configuration store somehow
}

func (s *ManagerService) Quit(stopTunnelsOnQuit bool, alreadyQuit *bool) error {
	if !atomic.CompareAndSwapUint32(&haveQuit, 0, 1) {
		*alreadyQuit = true
		return nil
	}
	*alreadyQuit = false

	// Work around potential race condition of delivering messages to the wrong process by removing from notifications.
	managerServicesLock.Lock()
	delete(managerServices, s)
	managerServicesLock.Unlock()

	if stopTunnelsOnQuit {
		names, err := conf.ListConfigNames()
		if err != nil {
			return err
		}
		for _, name := range names {
			UninstallTunnel(name)
		}
	}

	quitManagersChan <- struct{}{}
	return nil
}

func IPCServerListen(reader *os.File, writer *os.File, events *os.File) error {
	service := &ManagerService{events: events}

	server := rpc.NewServer()
	err := server.Register(service)
	if err != nil {
		return err
	}

	go func() {
		managerServicesLock.Lock()
		managerServices[service] = true
		managerServicesLock.Unlock()
		server.ServeConn(&pipeRWC{reader, writer})
		managerServicesLock.Lock()
		delete(managerServices, service)
		managerServicesLock.Unlock()

	}()
	return nil
}

func notifyAll(notificationType NotificationType, iface interface{}) {
	if len(managerServices) == 0 {
		return
	}

	var buf bytes.Buffer
	encoder := gob.NewEncoder(&buf)
	err := encoder.Encode(notificationType)
	if err != nil {
		return
	}
	if iface != nil {
		err = encoder.Encode(iface)
		if err != nil {
			return
		}
	}

	managerServicesLock.RLock()
	for m := range managerServices {
		go func() {
			m.events.SetWriteDeadline(time.Now().Add(time.Second))
			m.events.Write(buf.Bytes())
		}()
	}
	managerServicesLock.RUnlock()
}

func IPCServerNotifyTunnelChange(name string) {
	notifyAll(TunnelChangeNotificationType, name)
}

func IPCServerNotifyTunnelsChange() {
	notifyAll(TunnelsChangeNotificationType, nil)
}