Merge branch 'handle-push-continuation'

This commit is contained in:
Davide De Rosa 2019-04-17 00:50:45 +02:00
commit 537b733130
7 changed files with 58 additions and 21 deletions

View File

@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Negotiation times out with SoftEther. [#67](https://github.com/keeshux/tunnelkit/issues/67)
- Unable to handle continuated PUSH_REPLY. [#71](https://github.com/keeshux/tunnelkit/issues/71)
- TCP requiring multiple PUSH_REQUEST. [#73](https://github.com/keeshux/tunnelkit/issues/73)
## 1.6.1 (2019-04-07)

View File

@ -200,9 +200,9 @@ class NETCPLink: LinkInterface {
return maxPacketSize
}
let negotiationTimeout: TimeInterval = 10.0
let negotiationTimeout: TimeInterval = 60.0
let hardResetTimeout: TimeInterval = 5.0
let hardResetTimeout: TimeInterval = 20.0
func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
loopReadPackets(queue, Data(), handler)

View File

@ -106,6 +106,10 @@ public class ConfigurationParser {
static let externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
static let connection = NSRegularExpression("^<connection>")
// MARK: Continuation
static let continuation = NSRegularExpression("^push-continuation [12]")
}
private enum Topology: String {
@ -232,6 +236,16 @@ public class ConfigurationParser {
isHandled = true
}
// MARK: Continuation
var isContinuation = false
Regex.continuation.enumerateArguments(in: line) {
isContinuation = ($0.first == "2")
}
guard !isContinuation else {
throw SessionError.continuationPushReply
}
// MARK: Inline content
if unsupportedError == nil {

View File

@ -67,6 +67,8 @@ struct CoreConfiguration {
static let tickInterval = 0.2
static let pushRequestInterval = 2.0
static let pingTimeout = 120.0
static let retransmissionLimit = 0.1

View File

@ -59,6 +59,12 @@ public enum SessionError: String, Error {
/// The provided credentials failed authentication.
case badCredentials
/// The PUSH_REPLY is multipart.
case continuationPushReply
/// The reply to PUSH_REQUEST is malformed.
case malformedPushReply
/// A write operation failed at the link layer (e.g. network unreachable).
case failedLinkWrite

View File

@ -135,6 +135,8 @@ public class SessionProxy {
return link?.isReliable ?? false
}
private var continuatedPushReplyMessage: String?
private var pushReply: SessionReply?
private var nextPushRequestDate: Date?
@ -368,6 +370,7 @@ public class SessionProxy {
nextPushRequestDate = nil
connectedDate = nil
authenticator = nil
continuatedPushReplyMessage = nil
pushReply = nil
link = nil
if !(tunnel?.isPersistent ?? false) {
@ -403,12 +406,10 @@ public class SessionProxy {
return
}
if !isReliableLink {
pushRequest()
flushControlQueue()
}
pushRequest()
flushControlQueue()
guard (negotiationKey.controlState == .connected) else {
guard negotiationKey.controlState == .connected else {
queue.asyncAfter(deadline: .now() + CoreConfiguration.tickInterval) { [weak self] in
self?.loopNegotiation()
}
@ -607,6 +608,7 @@ public class SessionProxy {
log.debug("Send hard reset")
resetControlChannel(forNewSession: true)
continuatedPushReplyMessage = nil
pushReply = nil
negotiationKeyIdx = 0
let newKey = SessionKey(id: UInt8(negotiationKeyIdx))
@ -696,13 +698,11 @@ public class SessionProxy {
// Ruby: push_request
private func pushRequest() {
guard (negotiationKey.controlState == .preIfConfig) else {
guard negotiationKey.controlState == .preIfConfig else {
return
}
if !isReliableLink {
guard let targetDate = nextPushRequestDate, (Date() > targetDate) else {
return
}
guard let targetDate = nextPushRequestDate, Date() > targetDate else {
return
}
log.debug("TLS.ifconfig: Put plaintext (PUSH_REQUEST)")
@ -727,7 +727,7 @@ public class SessionProxy {
if negotiationKey.softReset {
completeConnection()
}
nextPushRequestDate = Date().addingTimeInterval(CoreConfiguration.retransmissionLimit)
nextPushRequestDate = Date().addingTimeInterval(CoreConfiguration.pushRequestInterval)
}
private func maybeRenegotiate() {
@ -854,8 +854,10 @@ public class SessionProxy {
}
do {
let controlData = try controlChannel.currentControlData(withTLS: negotiationKey.tls)
handleControlData(controlData)
while true {
let controlData = try controlChannel.currentControlData(withTLS: negotiationKey.tls)
handleControlData(controlData)
}
} catch _ {
}
}
@ -915,9 +917,15 @@ public class SessionProxy {
log.debug("Received control message: \"\(message)\"")
}
let completeMessage: String
if let continuated = continuatedPushReplyMessage {
completeMessage = "\(continuated),\(message)"
} else {
completeMessage = message
}
let reply: PushReply
do {
guard let optionalReply = try PushReply(message: message) else {
guard let optionalReply = try PushReply(message: completeMessage) else {
return
}
reply = optionalReply
@ -939,6 +947,10 @@ public class SessionProxy {
throw SessionError.serverCompression
}
}
} catch SessionError.continuationPushReply {
continuatedPushReplyMessage = completeMessage.replacingOccurrences(of: "push-continuation", with: "")
// FIXME: strip "PUSH_REPLY" and "push-continuation 2"
return
} catch let e {
deferStop(.shutdown, e)
return
@ -1143,11 +1155,6 @@ public class SessionProxy {
// MARK: Acks
private func handleAcks() {
// retry PUSH_REQUEST if ack queue is empty (all sent packets were ack'ed)
if isReliableLink && !controlChannel.hasPendingAcks() {
pushRequest()
}
}
// Ruby: send_ack

View File

@ -155,4 +155,10 @@ class PushTests: XCTestCase {
XCTAssertEqual(reply.options.keepAliveInterval, 10)
}
func testProvost() {
let msg = "PUSH_REPLY,route 87.233.192.218,route 87.233.192.219,route 87.233.192.220,route 87.248.186.252,route 92.241.171.245,route 103.246.200.0 255.255.252.0,route 109.239.140.0 255.255.255.0,route 128.199.0.0 255.255.0.0,route 13.125.0.0 255.255.0.0,route 13.230.0.0 255.254.0.0,route 13.56.0.0 255.252.0.0,route 149.154.160.0 255.255.252.0,route 149.154.164.0 255.255.252.0,route 149.154.168.0 255.255.252.0,route 149.154.172.0 255.255.252.0,route 159.122.128.0 255.255.192.0,route 159.203.0.0 255.255.0.0,route 159.65.0.0 255.255.0.0,route 159.89.0.0 255.255.0.0,route 165.227.0.0 255.255.0.0,route 167.99.0.0 255.255.0.0,route 174.138.0.0 255.255.128.0,route 176.67.169.0 255.255.255.0,route 178.239.88.0 255.255.248.0,route 178.63.0.0 255.255.0.0,route 18.130.0.0 255.255.0.0,route 18.144.0.0 255.255.0.0,route 18.184.0.0 255.254.0.0,route 18.194.0.0 255.254.0.0,route 18.196.0.0 255.254.0.0,route 18.204.0.0 255.252.0.0,push-continuation 2"
let reply = try! SessionProxy.PushReply(message: msg)!
reply.debug()
}
}