diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index 7841b0c5..206acfd2 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ using System.Windows; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.7.6.0")] -[assembly: AssemblyVersion("1.7.6.0")] -[assembly: AssemblyFileVersion("1.7.6.0")] +// [assembly: AssemblyVersion("1.7.8.0")] +[assembly: AssemblyVersion("1.7.8.0")] +[assembly: AssemblyFileVersion("1.7.8.0")] diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index 73373d6d..c1fcd02f 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -22,10 +22,10 @@ - + - + @@ -50,10 +50,12 @@ + + @@ -87,6 +89,8 @@ + + @@ -248,6 +252,8 @@ + + diff --git a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs index 81053e52..70a4ebe2 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/AbstractClient.cs @@ -227,12 +227,12 @@ namespace ZitiDesktopEdge.ServiceClient { return respAsString; } catch (IOException ioe) { //almost certainly a problem with the pipe - Logger.Debug("io error in read: " + ioe.Message); + Logger.Error(ioe, "io error in read: " + ioe.Message); ClientDisconnected(null); throw ioe; } catch (Exception ee) { //almost certainly a problem with the pipe - Logger.Debug("unexpected error in read: " + ee.Message); + Logger.Error(ee, "unexpected error in read: " + ee.Message); ClientDisconnected(null); throw ee; } diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 49d06c1f..6c862ebf 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -30,6 +30,6 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.7.6.0")] -[assembly: AssemblyVersion("1.7.6.0")] -[assembly: AssemblyFileVersion("1.7.6.0")] +// [assembly: AssemblyVersion("1.7.8.0")] +[assembly: AssemblyVersion("1.7.8.0")] +[assembly: AssemblyFileVersion("1.7.8.0")] diff --git a/ZitiUpdateService/UpdateService.cs b/ZitiUpdateService/UpdateService.cs index ee8a7442..5a7d2b08 100644 --- a/ZitiUpdateService/UpdateService.cs +++ b/ZitiUpdateService/UpdateService.cs @@ -17,6 +17,8 @@ using ZitiDesktopEdge.Utility; using NLog; using Newtonsoft.Json; +using DnsClient; +using System.Net; namespace ZitiUpdateService { public partial class UpdateService : ServiceBase { @@ -55,6 +57,9 @@ namespace ZitiUpdateService { Task eventServer = null; checkers.UpdateCheck check = null; + private Timer dnsProbeTimer = new Timer(); + private IPAddress dnsIpAddress = null; + public UpdateService() { InitializeComponent(); @@ -355,6 +360,9 @@ namespace ZitiUpdateService { AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge.exe.config")); //allow anyone to change the config file AccessUtils.GrantAccessToFile(Path.Combine(exeLocation, "ZitiDesktopEdge-log.config")); //allow anyone to change the log file config + dnsProbeTimer.Elapsed += DnsProbe_Elapsed; + dnsProbeTimer.Interval = dnsProbeIntervalInSeconds * 1000; + Logger.Info("starting ipc server"); ipcServer = svr.startIpcServerAsync(onIpcClientAsync); Logger.Info("starting events server"); @@ -371,7 +379,58 @@ namespace ZitiUpdateService { base.OnStart(args); } - async private Task onEventsClientAsync(StreamWriter writer) { + IPAddress lh = IPAddress.Parse("127.0.0.1"); //expected result + int dnsProbeFailCount = 0; + int dnsProbeIntervalInSeconds = 15; + bool dnsProbeStarted = false; + + private void DnsProbe_Elapsed(object sender, ElapsedEventArgs e) { + if (dnsProbeStarted) return; //skip out if it's already going... + dnsProbeStarted = true; + Logger.Trace("dns probe started"); + try { + if (dnsIpAddress != null) { + DnsQuestion q = new DnsQuestion("dew-dns-probe.openziti.org", QueryType.A); + var dnsEp = new IPEndPoint(dnsIpAddress, 53); + var dnsProbe = new LookupClient(dnsEp); + + foreach (DnsClient.Protocol.ARecord arec in dnsProbe.Query(q).AllRecords) { + if (arec.Address.Equals(lh)) { + dnsProbeFailCount = 0; + Logger.Debug("dns probe success"); + } else { + dnsProbeFailCount++; + logDnsProbeFailure(null); + } + } + } + } catch(Exception dnse) { + //don't really care but it probably means a timeout happened. but might as well log a trace error anyway... + //it's expected that this is due to the service shutting down... + dnsProbeFailCount++; + logDnsProbeFailure(dnse); + } + dnsProbeStarted = false; + } + + private void logDnsProbeFailure(Exception e) { + bool logit = false; + if (dnsProbeFailCount <= 4) { + logit = true; + } else { + //else log it every 5 minutes... + logit = dnsProbeFailCount % (5 * 60 / dnsProbeIntervalInSeconds) == 0; + } + if (logit) { + if (e != null) { + Logger.Warn(e, "dns probe failed due to error. This has happened {0} times", dnsProbeFailCount); + } else { + Logger.Warn("dns probe failed. This has happened {0} times", dnsProbeFailCount); + } + } + } + + async private Task onEventsClientAsync(StreamWriter writer) { Logger.Info("a new events client was connected"); //reset to release stream //initial status when connecting the event stream @@ -711,18 +770,30 @@ namespace ZitiUpdateService { EventRegistry.SendEventToConsumers(status); } - private static void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { + private void Svc_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { string dns = e?.Status?.IpInfo?.DNS; string version = e?.Status?.ServiceVersion.Version; string op = e?.Op; Logger.Info($"Operation {op}. running dns: {dns} at version {version}"); + + try { + dnsIpAddress = IPAddress.Parse(dns); + } catch { + //ignore it + } } - private static void Svc_OnClientConnected(object sender, object e) { + private void Svc_OnClientConnected(object sender, object e) { Logger.Info("successfully connected to service"); + if (!dnsProbeTimer.Enabled) { + dnsProbeTimer.Enabled = true; + dnsProbeTimer.Start(); + Logger.Info("DNS Probe enabled"); + } } private void Svc_OnClientDisconnected(object sender, object e) { + //dnsProbeTimer.Stop(); DataClient svc = (DataClient)sender; if (svc.CleanShutdown) { //then this is fine and expected - the service is shutting down diff --git a/ZitiUpdateService/ZitiUpdateService.csproj b/ZitiUpdateService/ZitiUpdateService.csproj index d2e99b4e..a2e7c843 100644 --- a/ZitiUpdateService/ZitiUpdateService.csproj +++ b/ZitiUpdateService/ZitiUpdateService.csproj @@ -76,6 +76,9 @@ bin\Releases\ + + ..\packages\DnsClient.1.3.2\lib\net471\DnsClient.dll + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll @@ -83,6 +86,9 @@ ..\packages\NLog.4.7.6\lib\net45\NLog.dll + + ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + diff --git a/ZitiUpdateService/checkers/GithubCheck.cs b/ZitiUpdateService/checkers/GithubCheck.cs index 352aa6db..eeca9452 100644 --- a/ZitiUpdateService/checkers/GithubCheck.cs +++ b/ZitiUpdateService/checkers/GithubCheck.cs @@ -15,7 +15,6 @@ namespace ZitiUpdateService.checkers { string url; string downloadUrl = null; string downloadFileName = null; - string currentResponse = null; Version nextVersion = null; public GithubCheck(string url) { diff --git a/ZitiUpdateService/packages.config b/ZitiUpdateService/packages.config index 04f986fe..18c2b3df 100644 --- a/ZitiUpdateService/packages.config +++ b/ZitiUpdateService/packages.config @@ -1,6 +1,8 @@  + + \ No newline at end of file diff --git a/release-notes.md b/release-notes.md index 0e5ff903..d213cdbe 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,34 @@ +# Release 1.7.9 + +## What's New +* nothing + +## Other changes: +* none + +## Bugs fixed: +* [#310](https://github.com/openziti/desktop-edge-win/pull/310) Restore identities moved by Windows after Windows system update + +## Dependency Updates +* Tunneler SDK to v0.8.21/CSDK 0.20.13 + +# Release 1.7.8 + +## What's New +* a new DNS probe record was added to the DNS server to allow DNS-related testing +* ziti-monitor service now probes the DNS server for diagnostic reasons +* added code to check upgrade status - only useful when the ziti-monitor service is not running + +## Other changes: +* minor logging updates + +## Bugs fixed: +* fixed an issue with hosting connections after channel failure [CSDK #233](https://github.com/openziti/ziti-sdk-c/pull/233) +* fixed a UI issue when no identites existed + +## Dependency Updates +* Tunneler SDK to v0.8.17/CSDK 0.20.12 + # Release 1.7.7 ## What's New @@ -11,6 +42,7 @@ ## Dependency Updates * Tunneler SDK to v0.8.15 + # Release 1.7.6 ## What's New diff --git a/service/cziti/dns.go b/service/cziti/dns.go index b7ce7a0a..627867c6 100644 --- a/service/cziti/dns.go +++ b/service/cziti/dns.go @@ -274,6 +274,15 @@ func DnsInit(tun api.DesktopEdgeIface, ip string, maskBits int) { dnsMgrPrivate.serviceMap = make(map[string]*ctxService) //DNS.ipMap = make(map[uint32]string) dnsMgrPrivate.hostnameMap = make(map[string]*ctxIp) + //register the test dns entry: + dnsMgrPrivate.hostnameMap[normalizeDnsName("dew-dns-probe.openziti.org")] = &ctxIp{ + //ctx: nil, + ip: net.ParseIP("127.0.0.1"), + //network: "", + dnsEnabled: true, + refCount: 0, + } + dnsip = net.ParseIP(ip).To4() mask := net.CIDRMask(maskBits, 32) dnsMgrPrivate.cidr = binary.BigEndian.Uint32(dnsip) & binary.BigEndian.Uint32(mask) diff --git a/service/ziti-tunnel/service/orphaned-identities.go b/service/ziti-tunnel/service/orphaned-identities.go new file mode 100644 index 00000000..af6f5c10 --- /dev/null +++ b/service/ziti-tunnel/service/orphaned-identities.go @@ -0,0 +1,101 @@ +package service + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/config" +) + +// After System update, the identity files are not getting copied to the config path +// So we are adding a function to scan for identities in the backtup location +func scanForIdentitiesPostWindowsUpdate() error { + srcBackUpPaths := [2]string{"Windows.~BT\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\NetFoundry", + "Windows.old\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\NetFoundry"} + systemDrivePath := os.Getenv("SystemDrive") + if systemDrivePath == "" { + return nil + } + for _, srcPath := range srcBackUpPaths { + sourcePath := filepath.Join(systemDrivePath, string(os.PathSeparator), srcPath) + _, err := os.Stat(sourcePath) + if err != nil { + log.Debugf("Folder %s does not exist", sourcePath) + continue + } + err = searchAndCopyFilesFromBackup(sourcePath) + if err != nil { + log.Debugf("Copy files from %s failed, %v", sourcePath, err) + } + } + return nil +} + +func searchAndCopyFilesFromBackup(srcPath string) error { + err := filepath.Walk(srcPath, copyFilesFromBackUp) + if err != nil { + return err + } + return nil +} + +func copyFilesFromBackUp(path string, f os.FileInfo, err error) error { + if !f.IsDir() { + log.Infof("Found: %s", path) + destinationFile := filepath.Join(config.Path(), string(os.PathSeparator), f.Name()) + //check if the file is present in the destination folder + _, err := os.Stat(destinationFile) + if err == nil && !strings.Contains(f.Name(), ConfigFileName) { + log.Debugf("File %s is already present in the config path and it is not %s, So not transfering", f.Name(), ConfigFileName) + deleteFile(path) + return nil + } + _, err = copy(path, destinationFile) + if err != nil { + log.Errorf("Error occurred while copying the Windows backup folder %s --> %s %v", f.Name(), destinationFile, err) + return nil + } else { + log.Infof("Found backup file at %s. Restored this file to %s", path, destinationFile) + deleteFile(path) + } + } + return nil +} +func deleteFile(path string) { + log.Infof("Removing file %s", path) + err := os.Remove(path) + if err != nil { + log.Errorf("Error occured while removing the file %s, %v", path, err) + } else { + log.Infof("Removed file %s", path) + } +} + +func copy(src, dst string) (int64, error) { + sourceFileStat, err := os.Stat(src) + if err != nil { + return 0, err + } + + if !sourceFileStat.Mode().IsRegular() { + return 0, errors.New(fmt.Sprintf("%s is not a regular file", src)) + } + + source, err := os.Open(src) + if err != nil { + return 0, err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return 0, err + } + defer destination.Close() + nBytes, err := io.Copy(destination, source) + return nBytes, err +} diff --git a/service/ziti-tunnel/service/pkg-vars.go b/service/ziti-tunnel/service/pkg-vars.go index 339b69a3..8636deb6 100644 --- a/service/ziti-tunnel/service/pkg-vars.go +++ b/service/ziti-tunnel/service/pkg-vars.go @@ -18,12 +18,14 @@ package service import ( + "sync" + "time" + "github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/util/logging" "golang.org/x/sys/windows/svc" - "sync" - "time" ) + var Version dto.ServiceVersion var pipeBase = `\\.\pipe\OpenZiti\ziti\` @@ -42,7 +44,7 @@ var Debug bool var TunStarted time.Time var log = logging.Logger() -var events = newTopic(32) +var events = newTopic(32) const ( API_VERSION = 1 @@ -60,13 +62,13 @@ const ( cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue InformationEvent = 0 //1 - ContinueEvent = 0 //2 - PauseEvent = 0 //3 - InstallEvent = 0 //4 + ContinueEvent = 0 //2 + PauseEvent = 0 //3 + InstallEvent = 0 //4 InterrogateEvent = 0 //5 - StopEvent = 0 //6 - ShutdownEvent = 0 //7 - ErrorEvent = 0 //1000 + StopEvent = 0 //6 + ShutdownEvent = 0 //7 + ErrorEvent = 0 //1000 // This is the name you will use for the NET START command SvcStartName = "ziti" @@ -88,7 +90,9 @@ const ( LocalService = "(A;;FA;;;LS)" NF_GROUP_NAME = "NetFoundry Tunneler Users" - TunName = "ZitiTUN" + TunName = "ZitiTUN" STATUS_ENROLLED = "enrolled" + + ConfigFileName = "config.json" ) diff --git a/service/ziti-tunnel/service/state.go b/service/ziti-tunnel/service/state.go index 4453dad5..9e83ec1c 100644 --- a/service/ziti-tunnel/service/state.go +++ b/service/ziti-tunnel/service/state.go @@ -21,6 +21,14 @@ import ( "bufio" "encoding/json" "fmt" + "io" + "io/ioutil" + "net" + "os" + "path" + "strings" + "time" + "github.com/openziti/desktop-edge-win/service/cziti" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/config" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/constants" @@ -29,13 +37,6 @@ import ( "golang.org/x/sys/windows/registry" "golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" - "io" - "io/ioutil" - "net" - "os" - "path" - "strings" - "time" ) type RuntimeState struct { @@ -58,7 +59,7 @@ func (t *RuntimeState) SaveState() { _ = os.MkdirAll(config.Path(), 0644) log.Debugf("backing up config") - backup,err := backupConfig() + backup, err := backupConfig() if err != nil { log.Warnf("could not backup config file! %v", err) } else { @@ -141,16 +142,16 @@ func (t *RuntimeState) ToStatus(onlyInitialized bool) dto.TunnelStatus { func (t *RuntimeState) ToMetrics() dto.TunnelStatus { clean := dto.TunnelStatus{ - Identities: make([]*dto.Identity, len(t.ids)), + Identities: make([]*dto.Identity, len(t.ids)), } i := 0 for _, id := range t.ids { AddMetrics(id) clean.Identities[i] = &dto.Identity{ - Name: id.Name, - FingerPrint: id.FingerPrint, - Metrics: id.Metrics, + Name: id.Name, + FingerPrint: id.FingerPrint, + Metrics: id.Metrics, } i++ } @@ -160,7 +161,7 @@ func (t *RuntimeState) ToMetrics() dto.TunnelStatus { func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device, error) { log.Infof("creating TUN device: %s", TunName) - tunDevice, err := tun.CreateTUN(TunName, 64*1024 - 1) + tunDevice, err := tun.CreateTUN(TunName, 64*1024-1) if err == nil { t.tun = &tunDevice tunName, err2 := tunDevice.Name() @@ -201,7 +202,7 @@ func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device return nil, nil, fmt.Errorf("failed to set IP address to %v: (%v)", ip, err) } - dnsServers := []net.IP{ ip } + dnsServers := []net.IP{ip} log.Infof("adding DNS servers to TUN: %s", dnsServers) err = luid.AddDNS(dnsServers) @@ -217,7 +218,7 @@ func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device log.Infof("TUN dns servers set to: %s", dns) log.Infof("setting routes for cidr: %s. Next Hop: %s", ipnet.String(), ipnet.IP.String()) - err = luid.SetRoutes([]*winipcfg.RouteData{{ Destination: *ipnet, NextHop: ipnet.IP, Metric: 0}}) + err = luid.SetRoutes([]*winipcfg.RouteData{{Destination: *ipnet, NextHop: ipnet.IP, Metric: 0}}) if err != nil { return nil, nil, fmt.Errorf("failed to SetRoutes: (%v)", err) } @@ -233,7 +234,7 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) { } _, err := os.Stat(id.Path()) - if err != nil{ + if err != nil { if os.IsNotExist(err) { //file does not exist. TODO remove this from the list } else { @@ -244,7 +245,7 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) { log.Infof("loading identity %s[%s]", id.Name, id.FingerPrint) - sc := func(status int){ + sc := func(status int) { log.Infof("status change! %d", status) id.ControllerVersion = id.CId.Version @@ -267,7 +268,7 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) { events.broadcast <- dto.IdentityEvent{ ActionEvent: IDENTITY_ADDED, - Id: id.Identity, + Id: id.Identity, } log.Infof("connecting identity completed: %s[%s]", id.Name, id.FingerPrint) } @@ -278,6 +279,7 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) { } func (t *RuntimeState) LoadConfig() { + scanForIdentitiesPostWindowsUpdate() err := readConfig(t, config.File()) if err != nil { err = readConfig(t, config.BackupFile()) @@ -319,7 +321,7 @@ func (t *RuntimeState) scanForOrphanedIdentities(folder string) { fingerprint := strings.Split(f.Name(), ".")[0] var found *dto.Identity for _, sid := range t.state.Identities { - if sid.FingerPrint == fingerprint{ + if sid.FingerPrint == fingerprint { found = sid break } @@ -387,11 +389,11 @@ func readConfig(t *RuntimeState, filename string) error { return nil } -func (t *RuntimeState) UpdateIpv4Mask(ipv4mask int){ +func (t *RuntimeState) UpdateIpv4Mask(ipv4mask int) { rts.state.TunIpv4Mask = ipv4mask rts.SaveState() } -func (t *RuntimeState) UpdateIpv4(ipv4 string){ +func (t *RuntimeState) UpdateIpv4(ipv4 string) { rts.state.TunIpv4 = ipv4 rts.SaveState() } @@ -447,7 +449,7 @@ func (t *RuntimeState) Close() { t.RemoveZitiTun() } -func(t *RuntimeState) RemoveZitiTun() { +func (t *RuntimeState) RemoveZitiTun() { log.Infof("Removing existing interface: %s", TunName) wt, err := tun.WintunPool.OpenAdapter(TunName) if err == nil { diff --git a/version b/version index 91c74a58..f65dc1e2 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.7.7 +1.7.9