diff --git a/CZiti.xcodeproj/project.pbxproj b/CZiti.xcodeproj/project.pbxproj index a43bc24..aa4759d 100644 --- a/CZiti.xcodeproj/project.pbxproj +++ b/CZiti.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 5A411E0B251FE40B005EE351 /* Bridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A0AC94E247783440021E8D9 /* Bridge.swift */; }; 5A5538E82556F92100C02F03 /* ZitiPostureChecks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A5538E72556F92100C02F03 /* ZitiPostureChecks.swift */; }; 5A5538E92556F92100C02F03 /* ZitiPostureChecks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A5538E72556F92100C02F03 /* ZitiPostureChecks.swift */; }; + 5A69FBFF25D1934A007B3B43 /* ZitiInterceptConfigV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A69FBFE25D1934A007B3B43 /* ZitiInterceptConfigV1.swift */; }; + 5A69FC0025D1934A007B3B43 /* ZitiInterceptConfigV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A69FBFE25D1934A007B3B43 /* ZitiInterceptConfigV1.swift */; }; 5A8B0B66258EDD1100182437 /* ZitiEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8B0B65258EDD1100182437 /* ZitiEvent.swift */; }; 5A8B0B67258EDD1100182437 /* ZitiEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8B0B65258EDD1100182437 /* ZitiEvent.swift */; }; 5A8B0B81258FEA9A00182437 /* ZitiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8B0B80258FEA9900182437 /* ZitiService.swift */; }; @@ -171,6 +173,7 @@ 5A0E4A8A24530E1E00C56DCF /* libziti.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libziti.a; path = "deps/ziti-sdk-c/build-os-arch/library/libziti.a"; sourceTree = ""; }; 5A0E4A8D24534DDE00C56DCF /* libsodium.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsodium.a; path = "deps/ziti-sdk-c/build-os-arch/_deps/libsodium-build/lib/libsodium.a"; sourceTree = ""; }; 5A5538E72556F92100C02F03 /* ZitiPostureChecks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZitiPostureChecks.swift; sourceTree = ""; }; + 5A69FBFE25D1934A007B3B43 /* ZitiInterceptConfigV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZitiInterceptConfigV1.swift; sourceTree = ""; }; 5A8B0B65258EDD1100182437 /* ZitiEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZitiEvent.swift; sourceTree = ""; }; 5A8B0B80258FEA9900182437 /* ZitiService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZitiService.swift; sourceTree = ""; }; 5A8B0C1C2594119900182437 /* ZitiTunnelServerConfigV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZitiTunnelServerConfigV1.swift; sourceTree = ""; }; @@ -389,6 +392,7 @@ 5AB83095247432C40089AF93 /* ZitiUrlClientConfigV1.swift */, 5AB83098247432C40089AF93 /* ZitiTunnelClientConfigV1.swift */, 5A8B0C1C2594119900182437 /* ZitiTunnelServerConfigV1.swift */, + 5A69FBFE25D1934A007B3B43 /* ZitiInterceptConfigV1.swift */, 5A5538E72556F92100C02F03 /* ZitiPostureChecks.swift */, 5AB8308F247432C40089AF93 /* ZitiError.swift */, 5AB83094247432C40089AF93 /* ZitiLog.swift */, @@ -655,6 +659,7 @@ 5AB830A9247432C50089AF93 /* ZitiIdentity.swift in Sources */, 5AA298BC2585961C001F7502 /* NetifDriver.swift in Sources */, 5AB830A3247432C40089AF93 /* ZitiLog.swift in Sources */, + 5A69FBFF25D1934A007B3B43 /* ZitiInterceptConfigV1.swift in Sources */, 5AB830A4247432C40089AF93 /* ZitiUrlClientConfigV1.swift in Sources */, 5AB830AA247432C50089AF93 /* Ziti.swift in Sources */, 5AB830AB247432C50089AF93 /* ziti.c in Sources */, @@ -683,6 +688,7 @@ 5AB830B7247433350089AF93 /* ZitiIdentity.swift in Sources */, 5AA298BD2585961C001F7502 /* NetifDriver.swift in Sources */, 5AB830B1247433200089AF93 /* ZitiLog.swift in Sources */, + 5A69FC0025D1934A007B3B43 /* ZitiInterceptConfigV1.swift in Sources */, 5AB830B2247433230089AF93 /* ZitiUrlClientConfigV1.swift in Sources */, 5AB830B8247433390089AF93 /* Ziti.swift in Sources */, 5AB830B92474333D0089AF93 /* ziti.c in Sources */, @@ -934,6 +940,9 @@ "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/libuv-src/include", "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/ziti-sdk-c-src/includes", "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/include", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/lib/lwip", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-src/src/include", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-contrib-src/ports/unix/port/include", ); LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/uv-mbed-build", @@ -959,6 +968,9 @@ "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/libuv-src/include", "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/ziti-sdk-c-src/includes", "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/include", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/lib/lwip", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-src/src/include", + "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-contrib-src/ports/unix/port/include", ); LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/uv-mbed-build", diff --git a/deps/ziti-tunnel-sdk-c b/deps/ziti-tunnel-sdk-c index 1c6e5f1..6dc4c65 160000 --- a/deps/ziti-tunnel-sdk-c +++ b/deps/ziti-tunnel-sdk-c @@ -1 +1 @@ -Subproject commit 1c6e5f19a6922fb00436e19934826b4e1fe434c8 +Subproject commit 6dc4c659d98e072b7faed6752fa023807723e59c diff --git a/lib/NetifDriver.swift b/lib/NetifDriver.swift index bd3bf88..a72fe26 100644 --- a/lib/NetifDriver.swift +++ b/lib/NetifDriver.swift @@ -61,7 +61,9 @@ class NetifDriver : NSObject, ZitiUnretained { driver.pointee.read = NetifDriver.read_cb // empty with log message. should never be called driver.pointee.write = NetifDriver.write_cb driver.pointee.close = NetifDriver.close_cb - + driver.pointee.add_route = NetifDriver.add_route_cb + driver.pointee.delete_route = NetifDriver.delete_route_cb + return driver } @@ -145,4 +147,36 @@ class NetifDriver : NSObject, ZitiUnretained { mySelf.close() return Int32(0) } + + static let add_route_cb:add_route_cb = { handle, dest in + guard let mySelf = zitiUnretained(NetifDriver.self, UnsafeMutableRawPointer(handle)) else { + log.wtf("invalid handle", function: "add_route_cb()") + return -1 + } + guard let tunnelProvider = mySelf.tunnelProvider else { + log.wtf("invalid tunnelProvider", function: "add_route_cb()") + return -1 + } + guard let destStr = dest != nil ? String(cString: dest!) : nil else { + log.error("Invalid dest paramater", function: "add_route_cb()") + return -1 + } + return tunnelProvider.addRoute(destStr) + } + + static let delete_route_cb:delete_route_cb = { handle, dest in + guard let mySelf = zitiUnretained(NetifDriver.self, UnsafeMutableRawPointer(handle)) else { + log.wtf("invalid handle", function: "delete_route_cb()") + return -1 + } + guard let tunnelProvider = mySelf.tunnelProvider else { + log.wtf("invalid tunnelProvider", function: "delete_route_cb()") + return -1 + } + guard let destStr = dest != nil ? String(cString: dest!) : nil else { + log.error("Invalid dest paramater", function: "delete_route_cb()") + return -1 + } + return tunnelProvider.deleteRoute(destStr) + } } diff --git a/lib/Ziti-Bridging-Header.h b/lib/Ziti-Bridging-Header.h index 28b7625..774efdb 100644 --- a/lib/Ziti-Bridging-Header.h +++ b/lib/Ziti-Bridging-Header.h @@ -21,10 +21,12 @@ limitations under the License. #include "ziti/ziti_tunnel_cbs.h" #include "ziti/netif_driver.h" +typedef int (*apply_cb)(dns_manager *dns, const char *host, const char *ip); + extern const char** ziti_all_configs; extern tls_context *default_tls_context(const char *ca, size_t ca_len); -void ziti_sdk_c_host_v1_wrapper(void *ziti_ctx, uv_loop_t *loop, const char *service_id, const char *proto, const char *hostname, int port); +tunneled_service_t *ziti_sdk_c_on_service_wrapper(ziti_context ziti_ctx, ziti_service *service, int status, tunneler_context tnlr_ctx); extern int ziti_log_level(void); extern void ziti_log_set_level(int level); diff --git a/lib/ZitiInterceptConfigV1.swift b/lib/ZitiInterceptConfigV1.swift new file mode 100644 index 0000000..2696c62 --- /dev/null +++ b/lib/ZitiInterceptConfigV1.swift @@ -0,0 +1,29 @@ +/* +Copyright 2021 NetFoundry, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import Foundation + +public class ZitiInterceptConfigV1 : Codable, ZitiConfig { + static var configType = "intercept.v1" + + public class PortRange : Codable { + let low:Int + let high:Int + } + + public let protocols:[String] + public let addresses:[String] + public let portRanges:[PortRange] +} diff --git a/lib/ZitiService.swift b/lib/ZitiService.swift index c29d342..af75ee2 100644 --- a/lib/ZitiService.swift +++ b/lib/ZitiService.swift @@ -33,6 +33,7 @@ import Foundation public var tunnelClientConfigV1:ZitiTunnelClientConfigV1? public var tunnelServerConfigV1:ZitiTunnelServerConfigV1? public var urlClientConfigV1:ZitiUrlClientConfigV1? + public var interceptConfigV1:ZitiInterceptConfigV1? init(_ cService:UnsafeMutablePointer) { self.cService = cService @@ -50,6 +51,9 @@ import Foundation if let cfg = ZitiService.parseConfig(ZitiUrlClientConfigV1.self, &(cService.pointee)) { urlClientConfigV1 = cfg } + if let cfg = ZitiService.parseConfig(ZitiInterceptConfigV1.self, &(cService.pointee)) { + interceptConfigV1 = cfg + } } static func parseConfig(_ type: T.Type, _ zs: inout ziti_service) -> T? where T:Decodable, T:ZitiConfig { diff --git a/lib/ZitiTunnel.swift b/lib/ZitiTunnel.swift index f944cbb..dfce166 100644 --- a/lib/ZitiTunnel.swift +++ b/lib/ZitiTunnel.swift @@ -16,6 +16,11 @@ limitations under the License. import Foundation public protocol ZitiTunnelProvider { + func addRoute(_ dest:String) -> Int32 + func deleteRoute(_ dest:String) -> Int32 + + func applyDns(_ host:String, _ ip:String) -> Int32 + func writePacket(_ data:Data) } @@ -25,10 +30,13 @@ public class ZitiTunnel : NSObject, ZitiUnretained { var tnlr_ctx:tunneler_context? var tunneler_opts:UnsafeMutablePointer! + var dns:UnsafeMutablePointer! let netifDriver:NetifDriver public init(_ tunnelProvider:ZitiTunnelProvider, _ loop:UnsafeMutablePointer) { netifDriver = NetifDriver(tunnelProvider: tunnelProvider) + super.init() + tunneler_opts = UnsafeMutablePointer.allocate(capacity: 1) tunneler_opts.initialize(to: tunneler_sdk_options( netif_driver: self.netifDriver.open(), @@ -36,12 +44,19 @@ public class ZitiTunnel : NSObject, ZitiUnretained { ziti_close: ziti_sdk_c_close, ziti_close_write: ziti_sdk_c_close_write, ziti_write: ziti_sdk_c_write, - ziti_host_v1: ziti_sdk_c_host_v1_wrapper)) + ziti_host: ziti_sdk_c_host)) tnlr_ctx = ziti_tunneler_init(tunneler_opts, loop) - super.init() + + dns = UnsafeMutablePointer.allocate(capacity: 1) + dns.initialize(to: dns_manager( + apply: ZitiTunnel.apply_dns_cb, + data: self.toVoidPtr())) + ziti_tunneler_set_dns(tnlr_ctx, dns) } deinit { + dns.deinitialize(count: 1) + dns.deallocate() tunneler_opts.deinitialize(count: 1) tunneler_opts.deallocate() } @@ -50,15 +65,18 @@ public class ZitiTunnel : NSObject, ZitiUnretained { netifDriver.queuePacket(data) } - public func v1Host(_ ziti_ctx: ziti_context?, _ service_name: UnsafePointer!, _ proto: UnsafePointer!, _ hostname: UnsafePointer!, _ port: Int32) -> Int32 { - return ziti_tunneler_host_v1(tnlr_ctx, UnsafeRawPointer(ziti_ctx), service_name, proto, hostname, port) - } - - public func v1Intercept(_ ziti_ctx: ziti_context?, _ service_id: UnsafePointer!, _ service_name: UnsafePointer!, _ hostname: UnsafePointer!, _ port: Int32) -> Int32 { - return ziti_tunneler_intercept_v1(tnlr_ctx, UnsafeRawPointer(ziti_ctx), service_id, service_name, hostname, port) + public func onService(_ ztx:ziti_context, _ svc: inout ziti_service, _ status:Int32) { + _ = ziti_sdk_c_on_service_wrapper(ztx, &svc, status, tnlr_ctx) } - public func v1StopIntercepting(_ service_id: UnsafePointer!) { - ziti_tunneler_stop_intercepting(tnlr_ctx, service_id) + static let apply_dns_cb:apply_cb = { dns, host, ip in + guard let mySelf = zitiUnretained(ZitiTunnel.self, dns?.pointee.data) else { + log.wtf("invalid context", function: "apply_dns_cb()") + return -1 + } + + let hostStr = host != nil ? String(cString: host!) : "" + let ipStr = ip != nil ? String(cString: ip!) : "" + return mySelf.netifDriver.tunnelProvider?.applyDns(hostStr, ipStr) ?? -1 } } diff --git a/lib/ZitiUrlProtocol.swift b/lib/ZitiUrlProtocol.swift index 50a10d8..2267a46 100644 --- a/lib/ZitiUrlProtocol.swift +++ b/lib/ZitiUrlProtocol.swift @@ -96,6 +96,33 @@ import Foundation } } + private class func interceptByHostAndPort(_ hostname:String, _ port:Int, _ ziti:Ziti, _ svcName:String, _ idleTime:Int) { + interceptsLock.lock() + defer { interceptsLock.unlock() } + + let hostPort = "\(hostname):\(port)" + if let curr = ZitiUrlProtocol.intercepts["http://\(hostPort)"] { + log.info("intercept \"http://\(hostPort)\" changing from \"\(curr.name)\" to \"\(svcName)\"", function:"onService()") + curr.close() + } + if let curr = ZitiUrlProtocol.intercepts["https://\(hostPort)"] { + log.info("intercept \"https://\(hostPort)\" changing from \"\(curr.name)\" to \"\(svcName)\"", function:"onService()") + curr.close() + } + + if let scheme = (port == 80 ? "http" : (port == 443 ? "https" : nil)) { + let intercept = ZitiIntercept(ziti, svcName, "\(scheme)://\(hostPort)", idleTime) + intercepts[intercept.urlStr] = intercept + log.info("Setting TUN intercept svc \(scheme)://\(hostPort): \(hostPort)", function:"onService()()") + } else { + var intercept = ZitiIntercept(ziti, svcName, "http://\(hostPort)", idleTime) + intercepts[intercept.urlStr] = intercept + intercept = ZitiIntercept(ziti, svcName, "https://\(hostPort)", idleTime) + intercepts[intercept.urlStr] = intercept + log.info("Setting TUN intercept svc \(svcName): \(hostPort)", function:"onService()()") + } + } + class func addOrUpdateService(_ svc:ZitiService, _ ziti:Ziti?, _ idleTime:Int) { guard let ziti = ziti, let svcName = svc.name else { log.wtf("invalid ziti reference or service name") @@ -123,32 +150,27 @@ import Foundation log.info("Setting URL intercept svc \(svcName): \(urlStr)") interceptsLock.unlock() - } else if let cfg = svc.tunnelClientConfigV1 { - let hostPort = "\(cfg.hostname):\(cfg.port)" - - interceptsLock.lock() - - if let curr = ZitiUrlProtocol.intercepts["http://\(hostPort)"] { - log.info("intercept \"http://\(hostPort)\" changing from \"\(curr.name)\" to \"\(svcName)\"", function:"onService()") - curr.close() - } - if let curr = ZitiUrlProtocol.intercepts["https://\(hostPort)"] { - log.info("intercept \"https://\(hostPort)\" changing from \"\(curr.name)\" to \"\(svcName)\"", function:"onService()") - curr.close() - } - - if let scheme = (cfg.port == 80 ? "http" : (cfg.port == 443 ? "https" : nil)) { - let intercept = ZitiIntercept(ziti, svcName, "\(scheme)://\(hostPort)", idleTime) - intercepts[intercept.urlStr] = intercept - log.info("Setting TUN intercept svc \(scheme)://\(hostPort): \(hostPort)", function:"onService()()") - } else { - var intercept = ZitiIntercept(ziti, svcName, "http://\(hostPort)", idleTime) - intercepts[intercept.urlStr] = intercept - intercept = ZitiIntercept(ziti, svcName, "https://\(hostPort)", idleTime) - intercepts[intercept.urlStr] = intercept - log.info("Setting TUN intercept svc \(svcName): \(hostPort)", function:"onService()()") + } else if let cfg = svc.interceptConfigV1, cfg.protocols.contains("tcp") { + cfg.addresses.forEach { addr in + for portRange in cfg.portRanges { + guard portRange.low <= portRange.high else { + log.error("invalid port range for service \(svcName), low=\(portRange.low), high=\(portRange.high)") + continue + } + + // warn if adding range of more than a few ports + let tot = portRange.high - portRange.low + if tot > 5 { + log.warn("Intercepting range of \(tot) total ports for service \(svcName) based on intercept.v1 config. Consider adding support for ziti-url-client") + } + + for port in portRange.low ... portRange.high { // possibly a very bad idea... + interceptByHostAndPort(addr, port, ziti, svcName, idleTime) + } + } } - interceptsLock.unlock() + } else if let cfg = svc.tunnelClientConfigV1 { + interceptByHostAndPort(cfg.hostname, cfg.port, ziti, svcName, idleTime) } else { log.warn("Ignoring service \(svcName). Unrecognized configuration") } diff --git a/lib/ziti.c b/lib/ziti.c index 2b74ebf..c5a7c19 100644 --- a/lib/ziti.c +++ b/lib/ziti.c @@ -25,8 +25,8 @@ static const char* _ziti_all[] = { const char** ziti_all_configs = _ziti_all; -void ziti_sdk_c_host_v1_wrapper(void *ziti_ctx, uv_loop_t *loop, const char *service_id, const char *proto, const char *hostname, int port) { - ziti_sdk_c_host_v1(ziti_ctx, loop, service_id, proto, hostname, port); +tunneled_service_t *ziti_sdk_c_on_service_wrapper(ziti_context ziti_ctx, ziti_service *service, int status, tunneler_context tnlr_ctx) { + return ziti_sdk_c_on_service(ziti_ctx, service, status, tnlr_ctx); } char **copyStringArray(char *const arr[], int count) {