/* SPDX-License-Identifier: MIT * * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. */ package service import ( "encoding/gob" "errors" "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 ) type NotificationType int const ( TunnelChangeNotificationType NotificationType = iota TunnelsChangeNotificationType ) var rpcClient *rpc.Client type TunnelChangeCallback struct { cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error) } var tunnelChangeCallbacks = make(map[*TunnelChangeCallback]bool) type TunnelsChangeCallback struct { cb func() } var tunnelsChangeCallbacks = make(map[*TunnelsChangeCallback]bool) func InitializeIPCClient(reader *os.File, writer *os.File, events *os.File) { rpcClient = rpc.NewClient(&pipeRWC{reader, writer}) go func() { decoder := gob.NewDecoder(events) for { var notificationType NotificationType err := decoder.Decode(¬ificationType) if err != nil { return } switch notificationType { case TunnelChangeNotificationType: var tunnel string err := decoder.Decode(&tunnel) if err != nil || len(tunnel) == 0 { continue } var state TunnelState err = decoder.Decode(&state) if err != nil { continue } var globalState TunnelState err = decoder.Decode(&globalState) if err != nil { continue } var errStr string err = decoder.Decode(&errStr) if err != nil { continue } var retErr error if len(errStr) > 0 { retErr = errors.New(errStr) } if state == TunnelUnknown { continue } t := &Tunnel{tunnel} for cb := range tunnelChangeCallbacks { cb.cb(t, state, globalState, retErr) } case TunnelsChangeNotificationType: for cb := range tunnelsChangeCallbacks { cb.cb() } } } }() } 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() error { return rpcClient.Call("ManagerService.Start", t.Name, nil) } func (t *Tunnel) Stop() error { return rpcClient.Call("ManagerService.Stop", t.Name, nil) } func (t *Tunnel) Toggle() (oldState TunnelState, err error) { oldState, err = t.State() if err != nil { oldState = TunnelUnknown return } if oldState == TunnelStarted { err = t.Stop() } else if oldState == TunnelStopped { err = t.Start() } return } func (t *Tunnel) WaitForStop() error { return rpcClient.Call("ManagerService.WaitForStop", t.Name, nil) } func (t *Tunnel) Delete() error { return rpcClient.Call("ManagerService.Delete", t.Name, nil) } 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", uintptr(0), &tunnels) } func IPCClientGlobalState() (TunnelState, error) { var state TunnelState return state, rpcClient.Call("ManagerService.GlobalState", uintptr(0), &state) } func IPCClientQuit(stopTunnelsOnQuit bool) (bool, error) { var alreadyQuit bool return alreadyQuit, rpcClient.Call("ManagerService.Quit", stopTunnelsOnQuit, &alreadyQuit) } func IPCClientRegisterTunnelChange(cb func(tunnel *Tunnel, state TunnelState, globalState TunnelState, err error)) *TunnelChangeCallback { s := &TunnelChangeCallback{cb} tunnelChangeCallbacks[s] = true return s } func (cb *TunnelChangeCallback) Unregister() { delete(tunnelChangeCallbacks, cb) } func IPCClientRegisterTunnelsChange(cb func()) *TunnelsChangeCallback { s := &TunnelsChangeCallback{cb} tunnelsChangeCallbacks[s] = true return s } func (cb *TunnelsChangeCallback) Unregister() { delete(tunnelsChangeCallbacks, cb) }