diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index da6d6f93..073d7a5b 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -230,7 +230,7 @@ namespace ZitiDesktopEdge { } if (e.Action == "added") { - ZitiService zs = new ZitiService(e.Service.Name, e.Service.HostName, e.Service.Port, ""); // CLINT: Gimme an error + ZitiService zs = new ZitiService(e.Service); var svc = found.Services.Find(s => s.Name == zs.Name); if (svc == null) found.Services.Add(zs); else Debug.WriteLine("the service named " + zs.Name + " is already accounted for on this identity."); diff --git a/DesktopEdge/Models/ZitiIdentity.cs b/DesktopEdge/Models/ZitiIdentity.cs index fd3361fa..5ba18ae1 100644 --- a/DesktopEdge/Models/ZitiIdentity.cs +++ b/DesktopEdge/Models/ZitiIdentity.cs @@ -44,7 +44,7 @@ namespace ZitiDesktopEdge.Models { if (id.Services != null) { foreach (var svc in id.Services) { - var zsvc = new ZitiService(svc.Name, svc.HostName, svc.Port, ""); // CLINT: Warning needed + var zsvc = new ZitiService(svc); zid.Services.Add(zsvc); } } diff --git a/DesktopEdge/Models/ZitiService.cs b/DesktopEdge/Models/ZitiService.cs index 4e7ba741..9b8f75eb 100644 --- a/DesktopEdge/Models/ZitiService.cs +++ b/DesktopEdge/Models/ZitiService.cs @@ -1,18 +1,55 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Management.Automation.Language; using System.Text; using System.Threading.Tasks; namespace ZitiDesktopEdge.Models { public class ZitiService { public string Name { get; set; } - public string Url { get; set; } - public string Warning { get; set; } - public ZitiService(string Name, string hostname, int port, string warning) { - this.Name = Name; - this.Url = hostname + ":" + port; - this.Warning = warning; + public string AssignedIP { get; set; } + public string Host { get; set; } + public string Port { get; set; } + public bool OwnsIntercept { get; set; } + public string Url + { + get + { + if (this.OwnsIntercept) + { + return Host + ":" + Port; + } else + { + return AssignedIP + ":" + Port; + } + } + } + public string Warning + { + get + { + if (this.OwnsIntercept) + { + return ""; + } + else + { + return $"Another identity already mapped the specified hostname: {Host}.\nThis service is only available via IP"; + } + } + } + + public ZitiService() { + } + + public ZitiService(ZitiDesktopEdge.ServiceClient.Service svc) + { + this.Name = svc.Name; + this.AssignedIP = svc.AssignedIP; + this.Host = svc.InterceptHost; + this.Port = svc.InterceptPort.ToString(); + this.OwnsIntercept = svc.OwnsIntercept; } } } diff --git a/DesktopEdge/ServiceClient/ClientImpl.cs b/DesktopEdge/ServiceClient/ClientImpl.cs index 428352b4..a656335c 100644 --- a/DesktopEdge/ServiceClient/ClientImpl.cs +++ b/DesktopEdge/ServiceClient/ClientImpl.cs @@ -122,8 +122,6 @@ namespace ZitiDesktopEdge.ServiceClient _extendedDebug = true; } } - //establish the named pipe to the service - setupPipe(); } catch(Exception ex) { diff --git a/DesktopEdge/ServiceClient/DataStructures.cs b/DesktopEdge/ServiceClient/DataStructures.cs index 6de9f0e9..2487e0a6 100644 --- a/DesktopEdge/ServiceClient/DataStructures.cs +++ b/DesktopEdge/ServiceClient/DataStructures.cs @@ -126,8 +126,10 @@ namespace ZitiDesktopEdge.ServiceClient public class Service { public string Name { get; set; } - public string HostName { get; set; } - public UInt16 Port { get; set; } + public string InterceptHost { get; set; } + public UInt16 InterceptPort { get; set; } + public bool OwnsIntercept { get; set; } + public string AssignedIP { get; set; } } public class EnrollmentFlags @@ -173,7 +175,7 @@ namespace ZitiDesktopEdge.ServiceClient writer.WriteLine($" Services:"); foreach (Service s in id.Services) { - writer.WriteLine($" Name: {s.Name} HostName: {s.HostName} Port: {s.Port}"); + writer.WriteLine($" Name: {s.Name} HostName: {s.InterceptHost} Port: {s.InterceptPort}"); } writer.WriteLine("============================================="); } diff --git a/release-notes.md b/release-notes.md index 55a2eace..6164e60b 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,18 +1,27 @@ +# Release 0.0.18 + +* What's New + + * [#70](https://github.com/openziti/desktop-edge-win/issues/70) - Version added to api to report when model changes occur + +* Bug fixes + + * [#69](https://github.com/openziti/desktop-edge-win/issues/69) - reference counting for identities with access to the same service + # Release 0.0.17 * What's New * [#70](https://github.com/openziti/desktop-edge-win/issues/70) - added api ApiVersion to TunnelStatusEvent * [#71](https://github.com/openziti/desktop-edge-win/issues/71) - add .log to rolled over log files - * * Bug fixes + * [#59](https://github.com/openziti/desktop-edge-win/issues/59) - too many services blocked service from accepting connections from the UI * [#61](https://github.com/openziti/desktop-edge-win/issues/61) - identity shutdown needs to be on the uv loop (issue with forgetting identities) * [#63](https://github.com/openziti/desktop-edge-win/issues/63) - when service restarts and UI reconnects clear identities and let the service repopulate the UI * [#67](https://github.com/openziti/desktop-edge-win/issues/67) - set the MTU based on the value reported from the interface - # Release 0.0.16 - tracking lost diff --git a/service/.gitignore b/service/.gitignore index 2eb53393..f291806f 100644 --- a/service/.gitignore +++ b/service/.gitignore @@ -1,4 +1,9 @@ build/ install/ *.exe -*.dll \ No newline at end of file +*.dll + +#files related to the service that shouldn't be pushed +NFWintunInstaller.msi +libgcc_s_seh-1.dll +libwinpthread-1.dll diff --git a/service/NFWintunInstaller.msi b/service/NFWintunInstaller.msi deleted file mode 100644 index 584e3504..00000000 Binary files a/service/NFWintunInstaller.msi and /dev/null differ diff --git a/service/build.bat b/service/build.bat index b336b1d0..d659d158 100644 --- a/service/build.bat +++ b/service/build.bat @@ -15,7 +15,7 @@ call %SVC_ROOT_DIR%/../get-ziti-ci.bat ziti-ci version echo generating version info - this will pushed from publish.bat in CI -ziti-ci generate-build-info --useVersion=false %SVC_ROOT_DIR%/ziti-tunnel/version.go main +ziti-ci generate-build-info --useVersion=false %SVC_ROOT_DIR%/ziti-tunnel/version.go main --verbose echo version info generated goto QUICK diff --git a/service/cziti/dns.go b/service/cziti/dns.go index a17e0b19..0eca8c90 100644 --- a/service/cziti/dns.go +++ b/service/cziti/dns.go @@ -17,9 +17,9 @@ package cziti +import "C" import ( "encoding/binary" - "errors" "fmt" "net" "sync" @@ -47,63 +47,108 @@ type dnsImpl struct { serviceMap map[string]ctxService // dnsName -> ip address - hostnameMap map[string]net.IP + hostnameMap map[string]ctxIp // ipv4 -> dnsName - ipMap map[uint32]string + //ipMap map[uint32]string +} + +type ctxIp struct { + ctx *CZitiCtx + ip net.IP + network string } type ctxService struct { - ctx *CZitiCtx - name string + ctx *CZitiCtx + name string + serviceId string + count int } -func (dns *dnsImpl) RegisterService(dnsName string, port uint16, ctx *CZitiCtx, name string) (net.IP, error) { - log.Infof("adding DNS entry for service name %s@%s:%d", name, dnsName, port) +// RegisterService will return the next ip address in the configured range. If the ip address is not +// assigned to a hostname an error will also be returned indicating why. +func (dns *dnsImpl) RegisterService(svcId string, dnsNameToReg string, port uint16, ctx *CZitiCtx, name string) (net.IP, error) { + log.Infof("adding DNS entry for service name %s@%s:%d", name, dnsNameToReg, port) DnsInit(defaultCidr, defaultMaskBits) - dnsName = dnsName + "." + dnsName := dnsNameToReg + "." key := fmt.Sprint(dnsName, ':', port) - if cs, found := dns.serviceMap[key]; found { - if cs.ctx != ctx || cs.name != name { - return nil, fmt.Errorf( - "service mapping conflict: %s:%d is already mapped for another context", dnsName, port) + + var ip net.IP + + currentNetwork := C.GoString(ctx.options.controller) + + // check to see if the hostname is mapped... + if foundIp, found := dns.hostnameMap[dnsName]; found { + ip = foundIp.ip + // now check to see if the host *and* port are mapped... + if foundContext, found := dns.serviceMap[key]; found { + if foundIp.network != currentNetwork { + // means the host:port are mapped to some other *identity* already. that's an invalid state + return ip, fmt.Errorf("service mapping conflict: %s:%d in %s is already mapped for another identity in %s", dnsNameToReg, port, currentNetwork, foundIp.network) + } + if foundContext.serviceId != svcId { + // means the host:port are mapped to some other service already. that's an invalid state + return ip, fmt.Errorf("service mapping conflict: %s:%d is already mapped for another service", dnsNameToReg, port) + } + // while the host *AND* port are not used - the hostname is. + // need to increment the refcounter of how many service use this hostname + foundContext.count ++ + log.Debugf("DNS mapping for %s used by another service. total services using %s = %d", dnsNameToReg, dnsNameToReg, foundContext.count) + } else { + // good - means the service can be mapped + dns.serviceMap[key] = ctxService{ + ctx: ctx, + name: name, + serviceId: svcId, + count: 1, + } + } + } else { + // if not used at all - map it + nextAddr := dns.cidr | atomic.AddUint32(&dns.counter, 1) + ip = make(net.IP, 4) + binary.BigEndian.PutUint32(ip, nextAddr) + + log.Infof("mapping hostname %s to ip %s", dnsNameToReg, ip.String()) + dns.hostnameMap[dnsName] = ctxIp { + ip: ip, + ctx: ctx, + network: currentNetwork, + } + dns.serviceMap[key] = ctxService{ + ctx: ctx, + name: name, + serviceId: svcId, + count: 1, } - return dns.hostnameMap[dnsName], nil } - dns.serviceMap[key] = ctxService{ - ctx: ctx, - name: name, - } - - if ip, mapped := dns.hostnameMap[dnsName]; mapped { - return ip, nil - } - - nextAddr := dns.cidr | atomic.AddUint32(&dns.counter, 1) - nextIp := make(net.IP, 4) - binary.BigEndian.PutUint32(nextIp, nextAddr) - dns.hostnameMap[dnsName] = nextIp - dns.ipMap[nextAddr] = dnsName - - return nextIp, nil + return ip, nil } func (dns *dnsImpl) Resolve(dnsName string) net.IP { - return dns.hostnameMap[dnsName] + return dns.hostnameMap[dnsName].ip } func (dns *dnsImpl) DeregisterService(ctx *CZitiCtx, name string) { for k, sc := range dns.serviceMap { if sc.ctx == ctx && sc.name == name { - log.Infof("removing %s from DNS mapping", name) - delete(dns.serviceMap, k) + if sc.count < 1 { + log.Infof("removing %s from DNS mapping", name) + delete(dns.serviceMap, k) + } else { + // another service is using the mapping - can't remove it yet so decrement + sc.count -- + log.Debugf("cannot remove dns mapping for %s yet - %d other services still use this hostname", name, sc.count) + } return } } } func (this *dnsImpl) GetService(ip net.IP, port uint16) (*CZitiCtx, string, error) { - ipv4 := binary.BigEndian.Uint32(ip) + return nil, "", nil //not used yet + /*ipv4 := binary.BigEndian.Uint32(ip) dns, found := this.ipMap[ipv4] if !found { return nil, "", errors.New("service not available") @@ -115,13 +160,14 @@ func (this *dnsImpl) GetService(ip net.IP, port uint16) (*CZitiCtx, string, erro return nil, "", errors.New("service not available") } return sc.ctx, sc.name, nil + */ } func DnsInit(ip string, maskBits int) { initOnce.Do(func() { DNS.serviceMap = make(map[string]ctxService) - DNS.ipMap = make(map[uint32]string) - DNS.hostnameMap = make(map[string]net.IP) + //DNS.ipMap = make(map[uint32]string) + DNS.hostnameMap = make(map[string]ctxIp) i := net.ParseIP(ip).To4() mask := net.CIDRMask(maskBits, 32) DNS.cidr = binary.BigEndian.Uint32(i) & binary.BigEndian.Uint32(mask) diff --git a/service/cziti/sdk.go b/service/cziti/sdk.go index a5d61b4a..83e1f822 100644 --- a/service/cziti/sdk.go +++ b/service/cziti/sdk.go @@ -99,6 +99,8 @@ type Service struct { Id string InterceptHost string InterceptPort uint16 + AssignedIP string + OwnsIntercept bool } type CZitiCtx struct { @@ -134,7 +136,7 @@ func (c *CZitiCtx) Controller() string { var tunCfgName = C.CString("ziti-tunneler-client.v1") //export serviceCB -func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data unsafe.Pointer) { +func serviceCB(_ C.ziti_context, service *C.ziti_service, status C.int, data unsafe.Pointer) { ctx := (*CZitiCtx)(data) if ctx.Services == nil { @@ -143,14 +145,14 @@ func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data un } name := C.GoString(service.name) - id := C.GoString(service.id) - log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v, %v ============", name, id, status, C.ZITI_SERVICE_UNAVAILABLE, C.ZITI_OK) + svcId := C.GoString(service.id) + log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v, %v ============", name, svcId, status, C.ZITI_SERVICE_UNAVAILABLE, C.ZITI_OK) if status == C.ZITI_SERVICE_UNAVAILABLE { - found, ok := ctx.Services.Load(id) + found, ok := ctx.Services.Load(svcId) fs := found.(Service) if ok { DNS.DeregisterService(ctx, name) - ctx.Services.Delete(id) + ctx.Services.Delete(svcId) ServiceChanges <- ServiceChange{ Operation: REMOVED, Service: &fs, @@ -172,23 +174,27 @@ func serviceCB(nf C.ziti_context, service *C.ziti_service, status C.int, data un port = int(c["port"].(float64)) } } - added := Service{ - Name: name, - Id: id, - InterceptHost: host, - InterceptPort: uint16(port), - } - ctx.Services.Store(id, added) if host != "" && port != -1 { - ip, err := DNS.RegisterService(host, uint16(port), ctx, name) + ownsIntercept := true + ip, err := DNS.RegisterService(svcId, host, uint16(port), ctx, name) if err != nil { log.Warn(err) + ownsIntercept = false } else { log.Infof("service intercept beginning for service: %s@%s:%d on ip %s", name, host, port, ip.String()) for _, t := range devMap { - t.AddIntercept(id, name, ip.String(), port, unsafe.Pointer(ctx.zctx)) + t.AddIntercept(svcId, name, ip.String(), port, unsafe.Pointer(ctx.zctx)) } } + added := Service{ + Name: name, + Id: svcId, + InterceptHost: host, + InterceptPort: uint16(port), + AssignedIP: ip.String(), + OwnsIntercept: ownsIntercept, + } + ctx.Services.Store(svcId, added) ServiceChanges <- ServiceChange{ Operation: ADDED, Service: &added, @@ -272,7 +278,6 @@ func(c *CZitiCtx) Shutdown() { var logFile *os.File //the current, active log file var logLevel int //set in InitializeCLogger -var lastRollover = time.Now() func InitializeCLogger(level int) { logLevel = level @@ -284,7 +289,6 @@ func InitializeCLogger(level int) { } func initiateRollLog() { - lastRollover = time.Now() async := (*C.uv_async_t)(C.malloc(C.sizeof_uv_async_t)) C.uv_async_init(_impl.libuvCtx.l, async, C.uv_async_cb(C.cron_callback)) C.uv_async_send((*C.uv_async_t)(unsafe.Pointer(async))) diff --git a/service/libgcc_s_seh-1.dll b/service/libgcc_s_seh-1.dll deleted file mode 100644 index abd357d6..00000000 Binary files a/service/libgcc_s_seh-1.dll and /dev/null differ diff --git a/service/libwinpthread-1.dll b/service/libwinpthread-1.dll deleted file mode 100644 index 500de9d4..00000000 Binary files a/service/libwinpthread-1.dll and /dev/null differ diff --git a/service/ziti-tunnel/service/debug.go b/service/ziti-tunnel/service/debug.go index bbfe7851..3c04a103 100644 --- a/service/ziti-tunnel/service/debug.go +++ b/service/ziti-tunnel/service/debug.go @@ -28,6 +28,7 @@ func dbg() { events.broadcast <- dto.TunnelStatusEvent{ StatusEvent: dto.StatusEvent{Op: "status"}, Status: r, + ApiVersion: API_VERSION, } svcs := make([]*dto.Service, 2) diff --git a/service/ziti-tunnel/service/ipc.go b/service/ziti-tunnel/service/ipc.go index 7148db11..c55c5eff 100644 --- a/service/ziti-tunnel/service/ipc.go +++ b/service/ziti-tunnel/service/ipc.go @@ -300,7 +300,7 @@ func accept(p net.Listener, serveFunction func(net.Conn), debug string) { func serveIpc(conn net.Conn) { log.Debug("beginning ipc receive loop") - defer log.Warn("IPC Loop has exited") + defer log.Info("A connected IPC client has disconnected") defer closeConn(conn) //close the connection after this function invoked as go routine exits events.broadcast <- dto.TunnelStatusEvent{ @@ -519,7 +519,7 @@ loop: case msg := <-consumer: err := o.Encode(msg) if err != nil { - log.Infof("exiting from serveEvents - %v", err) + log.Debugf("exiting from serveEvents - %v", err) break loop } _ = w.Flush() @@ -527,7 +527,7 @@ loop: break loop } } - log.Debug("exiting serve events") + log.Info("a connected event client has disconnected") } func reportStatus(out *json.Encoder) { @@ -828,6 +828,8 @@ func acceptServices() { InterceptHost: c.Service.InterceptHost, InterceptPort: c.Service.InterceptPort, Id: c.Service.Id, + AssignedIP: c.Service.AssignedIP, + OwnsIntercept: c.Service.OwnsIntercept, } id.Services = append(id.Services, &svc) diff --git a/version b/version index cd231804..32786aa4 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.0.17 +0.0.18