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 ### Fixed
- Negotiation times out with SoftEther. [#67](https://github.com/keeshux/tunnelkit/issues/67) - 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) ## 1.6.1 (2019-04-07)

View File

@ -200,9 +200,9 @@ class NETCPLink: LinkInterface {
return maxPacketSize 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) { func setReadHandler(queue: DispatchQueue, _ handler: @escaping ([Data]?, Error?) -> Void) {
loopReadPackets(queue, Data(), handler) 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 externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
static let connection = NSRegularExpression("^<connection>") static let connection = NSRegularExpression("^<connection>")
// MARK: Continuation
static let continuation = NSRegularExpression("^push-continuation [12]")
} }
private enum Topology: String { private enum Topology: String {
@ -232,6 +236,16 @@ public class ConfigurationParser {
isHandled = true 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 // MARK: Inline content
if unsupportedError == nil { if unsupportedError == nil {

View File

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

View File

@ -59,6 +59,12 @@ public enum SessionError: String, Error {
/// The provided credentials failed authentication. /// The provided credentials failed authentication.
case badCredentials 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). /// A write operation failed at the link layer (e.g. network unreachable).
case failedLinkWrite case failedLinkWrite

View File

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

View File

@ -155,4 +155,10 @@ class PushTests: XCTestCase {
XCTAssertEqual(reply.options.keepAliveInterval, 10) 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()
}
} }