aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/service
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2019-02-25 18:47:12 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-28 08:05:02 +0100
commita3054a01dfb6033b75b9ad31189b1202cbedcefc (patch)
treef57899e05cc73a1a5153cc6b118bf0bb810a8098 /service
parentservice: introduce base of services (diff)
downloadwireguard-windows-a3054a01dfb6033b75b9ad31189b1202cbedcefc.tar.xz
wireguard-windows-a3054a01dfb6033b75b9ad31189b1202cbedcefc.zip
ipc: add base of IPC
Diffstat (limited to 'service')
-rw-r--r--service/ipc_client.go87
-rw-r--r--service/ipc_event.go30
-rw-r--r--service/ipc_pipe.go58
-rw-r--r--service/ipc_server.go190
4 files changed, 365 insertions, 0 deletions
diff --git a/service/ipc_client.go b/service/ipc_client.go
new file mode 100644
index 00000000..25575014
--- /dev/null
+++ b/service/ipc_client.go
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "net/rpc"
+ "os"
+)
+
+type Tunnel struct {
+ Name string
+}
+
+type TunnelState int
+
+const (
+ TunnelUnknown TunnelState = iota
+ TunnelStarted
+ TunnelStopped
+ TunnelStarting
+ TunnelStopping
+ TunnelDeleting
+)
+
+var rpcClient *rpc.Client
+
+func InitializeIPCClient(reader *os.File, writer *os.File) {
+ rpcClient = rpc.NewClient(&pipeRWC{reader, writer})
+}
+
+func (t *Tunnel) StoredConfig() (c conf.Config, err error) {
+ err = rpcClient.Call("ManagerService.StoredConfig", t.Name, &c)
+ return
+}
+
+func (t *Tunnel) RuntimeConfig() (c conf.Config, err error) {
+ err = rpcClient.Call("ManagerService.RuntimeConfig", t.Name, &c)
+ return
+}
+
+func (t *Tunnel) Start() (TunnelState, error) {
+ var state TunnelState
+ return state, rpcClient.Call("ManagerService.Start", t.Name, &state)
+}
+
+func (t *Tunnel) Stop() (TunnelState, error) {
+ var state TunnelState
+ return state, rpcClient.Call("ManagerService.Stop", t.Name, &state)
+}
+
+func (t *Tunnel) Delete() (TunnelState, error) {
+ var state TunnelState
+ return state, rpcClient.Call("ManagerService.Delete", t.Name, &state)
+}
+
+func (t *Tunnel) State() (TunnelState, error) {
+ var state TunnelState
+ return state, rpcClient.Call("ManagerService.State", t.Name, &state)
+}
+
+func IPCClientNewTunnel(conf *conf.Config) (Tunnel, error) {
+ var tunnel Tunnel
+ return tunnel, rpcClient.Call("ManagerService.Create", *conf, &tunnel)
+}
+
+func IPCClientTunnels() ([]Tunnel, error) {
+ var tunnels []Tunnel
+ return tunnels, rpcClient.Call("ManagerService.Tunnels", 0, &tunnels)
+}
+
+func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) {
+ var alreadyQuit bool
+ return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit)
+}
+
+func IPCClientRegisterAsNotificationThread() error {
+ return rpcClient.Call("ManagerService.RegisterAsNotificationThread", windows.GetCurrentThreadId(), nil)
+}
+
+func IPCClientUnregisterAsNotificationThread() error {
+ return rpcClient.Call("ManagerService.UnregisterAsNotificationThread", windows.GetCurrentThreadId(), nil)
+}
diff --git a/service/ipc_event.go b/service/ipc_event.go
new file mode 100644
index 00000000..f56f289d
--- /dev/null
+++ b/service/ipc_event.go
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import "golang.org/x/sys/windows"
+
+//sys registerWindowMessage(name *uint16) (message uint, err error) = user32.RegisterWindowMessageW
+
+var (
+ tunnelsChangedMessage uint
+ tunnelChangedMessage uint
+)
+func IPCRegisterEventMessages() error {
+ m, err := registerWindowMessage(windows.StringToUTF16Ptr("WireGuard Manager Event - Tunnels Changed"))
+ if err != nil {
+ return err
+ }
+ tunnelsChangedMessage = m
+
+ m, err = registerWindowMessage(windows.StringToUTF16Ptr("WireGuard Manager Event - Tunnel Changed"))
+ if err != nil {
+ return err
+ }
+ tunnelChangedMessage = m
+
+ return nil
+}
diff --git a/service/ipc_pipe.go b/service/ipc_pipe.go
new file mode 100644
index 00000000..ee63f2d4
--- /dev/null
+++ b/service/ipc_pipe.go
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "golang.org/x/sys/windows"
+ "os"
+ "strconv"
+)
+
+type pipeRWC struct {
+ reader *os.File
+ writer *os.File
+}
+
+func (p *pipeRWC) Read(b []byte) (int, error) {
+ return p.reader.Read(b)
+}
+
+func (p *pipeRWC) Write(b []byte) (int, error) {
+ return p.writer.Write(b)
+}
+
+func (p *pipeRWC) Close() error {
+ err1 := p.writer.Close()
+ err2 := p.reader.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+func inheritableSocketpairEmulation() (ourReader *os.File, theirReader *os.File, theirReaderStr string, ourWriter *os.File, theirWriter *os.File, theirWriterStr string, err error) {
+ ourReader, theirWriter, err = os.Pipe()
+ if err != nil {
+ return
+ }
+ err = windows.SetHandleInformation(windows.Handle(theirWriter.Fd()), windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT)
+ if err != nil {
+ return
+ }
+ theirWriterStr = strconv.FormatUint(uint64(theirWriter.Fd()), 10)
+
+ theirReader, ourWriter, err = os.Pipe()
+ if err != nil {
+ return
+ }
+ err = windows.SetHandleInformation(windows.Handle(theirReader.Fd()), windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT)
+ if err != nil {
+ return
+ }
+ theirReaderStr = strconv.FormatUint(uint64(theirReader.Fd()), 10)
+
+ return
+}
diff --git a/service/ipc_server.go b/service/ipc_server.go
new file mode 100644
index 00000000..a2a4c9ee
--- /dev/null
+++ b/service/ipc_server.go
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "errors"
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "net/rpc"
+ "os"
+ "sync"
+ "sync/atomic"
+)
+
+var managerServices = make(map[*ManagerService]bool)
+var managerServicesLock sync.RWMutex
+var haveQuit uint32
+var quitManagersChan = make(chan struct{}, 1)
+
+type ManagerService struct {
+ notifierHandles map[windows.Handle]bool
+ notifierHandlesLock sync.RWMutex
+}
+
+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 (s *ManagerService) RegisterAsNotificationThread(handle windows.Handle, unused *uintptr) error {
+ s.notifierHandlesLock.Lock()
+ s.notifierHandles[handle] = true
+ s.notifierHandlesLock.Unlock()
+ return nil
+}
+
+func (s *ManagerService) UnregisterAsNotificationThread(handle windows.Handle, unused *uintptr) error {
+ s.notifierHandlesLock.Lock()
+ delete(s.notifierHandles, handle)
+ s.notifierHandlesLock.Unlock()
+ return nil
+}
+
+func IPCServerListen(reader *os.File, writer *os.File) error {
+ service := &ManagerService{notifierHandles: make(map[windows.Handle]bool)}
+
+ 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
+}
+
+//sys postMessage(hwnd windows.Handle, msg uint, wparam uintptr, lparam uintptr) (err error) = user32.PostMessageW
+
+func notifyAll(f func(handle windows.Handle)) {
+ managerServicesLock.RLock()
+ for m, _ := range managerServices {
+ m.notifierHandlesLock.RLock()
+ for handle, _ := range m.notifierHandles {
+ f(handle)
+ }
+ m.notifierHandlesLock.RUnlock()
+ }
+ managerServicesLock.RUnlock()
+}
+
+func IPCServerNotifyTunnelChange(name string) {
+ notifyAll(func(handle windows.Handle) {
+ //TODO: postthreadmessage
+ })
+}
+
+func IPCServerNotifyTunnelsChange() {
+ notifyAll(func(handle windows.Handle) {
+ postMessage(handle, tunnelsChangedMessage, 0, 0)
+ })
+}