diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1e89e..9f64bb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Send explicit exit notification if UDP. [#29](https://github.com/keeshux/tunnelkit/issues/29) + ## 1.5.0 (2019-03-20) ### Added diff --git a/TunnelKit/Sources/Core/Packet.swift b/TunnelKit/Sources/Core/Packet.swift index 036f5fb..b934f61 100644 --- a/TunnelKit/Sources/Core/Packet.swift +++ b/TunnelKit/Sources/Core/Packet.swift @@ -128,6 +128,22 @@ class DataPacket { static let pingString = Data(hex: "2a187bf3641eb4cb07ed2d0a981fc748") } +enum OCCPacket: UInt8 { + case exit = 0x06 + + private static let magicString = Data(hex: "287f346bd4ef7a812d56b8d3afc5459c") + + func serialized(_ info: Any? = nil) -> Data { + var data = OCCPacket.magicString + data.append(rawValue) + switch self { + case .exit: + break // nothing more + } + return data + } +} + /// :nodoc: extension PacketCode: CustomStringConvertible { public var description: String { diff --git a/TunnelKit/Sources/Core/SessionProxy.swift b/TunnelKit/Sources/Core/SessionProxy.swift index 366daa0..da820b6 100644 --- a/TunnelKit/Sources/Core/SessionProxy.swift +++ b/TunnelKit/Sources/Core/SessionProxy.swift @@ -992,8 +992,8 @@ public class SessionProxy { self?.queue.sync { log.error("Failed LINK write during control flush: \(error)") self?.deferStop(.shutdown, SessionError.failedLinkWrite) - return } + return } } } @@ -1115,8 +1115,8 @@ public class SessionProxy { self?.queue.sync { log.error("Data: Failed LINK write during send data: \(error)") self?.deferStop(.shutdown, SessionError.failedLinkWrite) - return } + return } // log.verbose("Data: \(encryptedPackets.count) packets successfully written to LINK") } @@ -1161,8 +1161,8 @@ public class SessionProxy { self?.queue.sync { log.error("Failed LINK write during send ack for packetId \(controlPacket.packetId): \(error)") self?.deferStop(.shutdown, SessionError.failedLinkWrite) - return } + return } log.debug("Ack successfully written to LINK for packetId \(controlPacket.packetId)") } @@ -1176,13 +1176,32 @@ public class SessionProxy { private func deferStop(_ method: StopMethod, _ error: Error?) { isStopping = true - - switch method { - case .shutdown: - doShutdown(error: error) - - case .reconnect: - doReconnect(error: error) + + let completion = { [weak self] in + switch method { + case .shutdown: + self?.doShutdown(error: error) + + case .reconnect: + self?.doReconnect(error: error) + } + } + + // shut down after sending exit notification if socket is unreliable (normally UDP) + if let link = link, !link.isReliable { + do { + guard let packets = try currentKey?.encrypt(packets: [OCCPacket.exit.serialized()]) else { + completion() + return + } + link.writePackets(packets) { (error) in + completion() + } + } catch { + completion() + } + } else { + completion() } }