From a3054a01dfb6033b75b9ad31189b1202cbedcefc Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 25 Feb 2019 18:47:12 +0100 Subject: ipc: add base of IPC --- service/ipc_server.go | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 service/ipc_server.go (limited to 'service/ipc_server.go') 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) + }) +} -- cgit v1.2.3-59-g8ed1b