Merge pull request #70 from keeshux/support-compress-lzo
Support --compress lzo
This commit is contained in:
commit
76631a00fc
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Support for legacy `--comp-lzo` compression. [#69](https://github.com/keeshux/tunnelkit/pull/69)
|
- Support for legacy `--comp-lzo` compression. [#69](https://github.com/keeshux/tunnelkit/pull/69)
|
||||||
|
- Support for newer `--compress lzo` option. [#70](https://github.com/keeshux/tunnelkit/pull/70)
|
||||||
|
|
||||||
## 1.4.3 (2019-03-18)
|
## 1.4.3 (2019-03-18)
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -29,11 +29,10 @@ The client is known to work with [OpenVPN®][openvpn] 2.3+ servers.
|
|||||||
- Authentication (`--tls-auth`)
|
- Authentication (`--tls-auth`)
|
||||||
- Encryption (`--tls-crypt`)
|
- Encryption (`--tls-crypt`)
|
||||||
- [x] Compression framing
|
- [x] Compression framing
|
||||||
- Disabled
|
- Via `--comp-lzo` (deprecated in 2.4)
|
||||||
- Compress (2.4)
|
- Via `--compress`
|
||||||
- LZO (deprecated in 2.4)
|
|
||||||
- [x] Compression algorithms
|
- [x] Compression algorithms
|
||||||
- LZO (`--comp-lzo` only)
|
- LZO (via `--comp-lzo` or `--compress lzo`)
|
||||||
- [x] Key renegotiation
|
- [x] Key renegotiation
|
||||||
- [x] Replay protection (hardcoded window)
|
- [x] Replay protection (hardcoded window)
|
||||||
|
|
||||||
@ -46,8 +45,7 @@ TunnelKit can parse .ovpn configuration files. Below are a few limitations worth
|
|||||||
Unsupported:
|
Unsupported:
|
||||||
|
|
||||||
- UDP fragmentation, i.e. `--fragment`
|
- UDP fragmentation, i.e. `--fragment`
|
||||||
- Compression
|
- Compression via `--compress` other than empty or `lzo`
|
||||||
- `--compress` other than empty
|
|
||||||
- Proxy
|
- Proxy
|
||||||
- External file references (inline `<block>` only)
|
- External file references (inline `<block>` only)
|
||||||
- Encrypted client certificate keys
|
- Encrypted client certificate keys
|
||||||
|
@ -37,9 +37,9 @@ Pod::Spec.new do |s|
|
|||||||
s.subspec "LZO" do |p|
|
s.subspec "LZO" do |p|
|
||||||
p.source_files = "TunnelKit/Sources/Core/LZO.h",
|
p.source_files = "TunnelKit/Sources/Core/LZO.h",
|
||||||
"TunnelKit/Sources/Core/Errors.{h,m}",
|
"TunnelKit/Sources/Core/Errors.{h,m}",
|
||||||
"TunnelKit/Sources/LZO/**/*.{h,m,c}"
|
"TunnelKit/Sources/LZO/**/*lzo*.{h,m,c}"
|
||||||
p.private_header_files = "TunnelKit/Sources/Core/LZO.h",
|
p.private_header_files = "TunnelKit/Sources/Core/LZO.h",
|
||||||
"TunnelKit/Sources/LZO/lib/*.h"
|
"TunnelKit/Sources/LZO/lib/*lzo*.h"
|
||||||
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -315,11 +315,18 @@ public class ConfigurationParser {
|
|||||||
isHandled = true
|
isHandled = true
|
||||||
compressionFraming = .compress
|
compressionFraming = .compress
|
||||||
|
|
||||||
|
if !LZOIsSupported() {
|
||||||
guard $0.isEmpty else {
|
guard $0.isEmpty else {
|
||||||
compressionAlgorithm = .other
|
|
||||||
unsupportedError = .unsupportedConfiguration(option: line)
|
unsupportedError = .unsupportedConfiguration(option: line)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if let arg = $0.first {
|
||||||
|
compressionAlgorithm = (arg == "lzo") ? .LZO : .other
|
||||||
|
} else {
|
||||||
|
compressionAlgorithm = .disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regex.keyDirection.enumerateArguments(in: line) {
|
Regex.keyDirection.enumerateArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
|
@ -117,7 +117,7 @@
|
|||||||
[self.decrypter setPeerId:peerId];
|
[self.decrypter setPeerId:peerId];
|
||||||
[self setCompressionFraming:compressionFraming];
|
[self setCompressionFraming:compressionFraming];
|
||||||
|
|
||||||
if (LZOIsSupported() && (compressionFraming == CompressionFramingNativeCompLZO) && (compressionAlgorithm == CompressionAlgorithmNativeLZO)) {
|
if (LZOIsSupported() && (compressionAlgorithm == CompressionAlgorithmNativeLZO)) {
|
||||||
self.lzo = LZOCreate();
|
self.lzo = LZOCreate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +168,42 @@
|
|||||||
|
|
||||||
- (void)setCompressionFraming:(CompressionFramingNative)compressionFraming
|
- (void)setCompressionFraming:(CompressionFramingNative)compressionFraming
|
||||||
{
|
{
|
||||||
|
__weak DataPath *weakSelf = self;
|
||||||
|
|
||||||
|
DataPathParseBlock parseCompressedBlock = ^BOOL(uint8_t * _Nonnull payload, NSInteger * _Nonnull payloadOffset, uint8_t * _Nonnull compressionHeader, NSInteger * _Nonnull headerLength, const uint8_t * _Nonnull packet, NSInteger packetLength, NSError * _Nullable __autoreleasing * _Nullable error) {
|
||||||
|
*compressionHeader = payload[0];
|
||||||
|
*headerLength = 1;
|
||||||
|
|
||||||
|
switch (*compressionHeader) {
|
||||||
|
case DataPacketNoCompress:
|
||||||
|
*payloadOffset = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DataPacketNoCompressSwap:
|
||||||
|
payload[0] = packet[packetLength - 1];
|
||||||
|
*payloadOffset = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DataPacketLZOCompress:
|
||||||
|
if (!weakSelf.lzo) { // compressed packet unexpected
|
||||||
|
if (error) {
|
||||||
|
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeDataPathCompression);
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
*payloadOffset = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// @"Expected NO_COMPRESS (found %X != %X)", payload[0], DataPacketNoCompress);
|
||||||
|
if (error) {
|
||||||
|
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeDataPathCompression);
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
};
|
||||||
|
|
||||||
switch (compressionFraming) {
|
switch (compressionFraming) {
|
||||||
case CompressionFramingNativeDisabled: {
|
case CompressionFramingNativeDisabled: {
|
||||||
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
||||||
@ -184,27 +220,30 @@
|
|||||||
}
|
}
|
||||||
case CompressionFramingNativeCompress: {
|
case CompressionFramingNativeCompress: {
|
||||||
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
||||||
|
NSData *compressedPayload = [weakSelf.lzo compressedDataWithData:payload error:NULL];
|
||||||
|
if (compressedPayload) {
|
||||||
|
packetDest[0] = DataPacketLZOCompress;
|
||||||
|
*packetLengthOffset = 1 - (payload.length - compressedPayload.length);
|
||||||
|
payload = compressedPayload;
|
||||||
|
memcpy(packetDest + 1, payload.bytes, payload.length);
|
||||||
|
} else {
|
||||||
|
*packetLengthOffset = 1;
|
||||||
|
|
||||||
|
// do not byte swap if compression enabled
|
||||||
|
if (weakSelf.lzo) {
|
||||||
|
packetDest[0] = DataPacketNoCompress;
|
||||||
|
memcpy(packetDest + 1, payload.bytes, payload.length);
|
||||||
|
} else {
|
||||||
memcpy(packetDest, payload.bytes, payload.length);
|
memcpy(packetDest, payload.bytes, payload.length);
|
||||||
packetDest[payload.length] = packetDest[0];
|
packetDest[payload.length] = packetDest[0];
|
||||||
packetDest[0] = DataPacketNoCompressSwap;
|
packetDest[0] = DataPacketNoCompressSwap;
|
||||||
*packetLengthOffset = 1;
|
|
||||||
};
|
|
||||||
self.parsePayloadBlock = ^BOOL(uint8_t * _Nonnull payload, NSInteger * _Nonnull payloadOffset, uint8_t * _Nonnull compressionHeader, NSInteger * _Nonnull headerLength, const uint8_t * _Nonnull packet, NSInteger packetLength, NSError * _Nullable __autoreleasing * _Nullable error) {
|
|
||||||
*compressionHeader = payload[0];
|
|
||||||
if (*compressionHeader != DataPacketNoCompressSwap) {
|
|
||||||
// @"Expected NO_COMPRESS_SWAP (found %X != %X)", payload[0], DataPacketNoCompressSwap);
|
|
||||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeDataPathCompression);
|
|
||||||
return NO;
|
|
||||||
}
|
}
|
||||||
payload[0] = packet[packetLength - 1];
|
}
|
||||||
*payloadOffset = 0;
|
|
||||||
*headerLength = 1;
|
|
||||||
return YES;
|
|
||||||
};
|
};
|
||||||
|
self.parsePayloadBlock = parseCompressedBlock;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CompressionFramingNativeCompLZO: {
|
case CompressionFramingNativeCompLZO: {
|
||||||
__weak DataPath *weakSelf = self;
|
|
||||||
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
|
||||||
NSData *compressedPayload = [weakSelf.lzo compressedDataWithData:payload error:NULL];
|
NSData *compressedPayload = [weakSelf.lzo compressedDataWithData:payload error:NULL];
|
||||||
if (compressedPayload) {
|
if (compressedPayload) {
|
||||||
@ -217,32 +256,7 @@
|
|||||||
}
|
}
|
||||||
memcpy(packetDest + 1, payload.bytes, payload.length);
|
memcpy(packetDest + 1, payload.bytes, payload.length);
|
||||||
};
|
};
|
||||||
self.parsePayloadBlock = ^BOOL(uint8_t * _Nonnull payload, NSInteger * _Nonnull payloadOffset, uint8_t * _Nonnull compressionHeader, NSInteger * _Nonnull headerLength, const uint8_t * _Nonnull packet, NSInteger packetLength, NSError * _Nullable __autoreleasing * _Nullable error) {
|
self.parsePayloadBlock = parseCompressedBlock;
|
||||||
*compressionHeader = payload[0];
|
|
||||||
switch (*compressionHeader) {
|
|
||||||
case DataPacketNoCompress:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DataPacketLZOCompress:
|
|
||||||
if (!LZOIsSupported() || !weakSelf.lzo) { // compressed packet unexpected
|
|
||||||
if (error) {
|
|
||||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeDataPathCompression);
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// @"Expected NO_COMPRESS (found %X != %X)", payload[0], DataPacketNoCompress);
|
|
||||||
if (error) {
|
|
||||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeDataPathCompression);
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
*payloadOffset = 1;
|
|
||||||
*headerLength = 1;
|
|
||||||
return YES;
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,18 +395,20 @@ extension SessionProxy {
|
|||||||
switch $0[0] {
|
switch $0[0] {
|
||||||
case "comp-lzo":
|
case "comp-lzo":
|
||||||
compressionFraming = .compLZO
|
compressionFraming = .compLZO
|
||||||
if !(($0.count == 2) && ($0[1] == "no")) {
|
if ($0.count == 2) && ($0[1] == "no") {
|
||||||
compressionAlgorithm = .LZO
|
|
||||||
} else {
|
|
||||||
compressionAlgorithm = .disabled
|
compressionAlgorithm = .disabled
|
||||||
|
} else {
|
||||||
|
compressionAlgorithm = .LZO
|
||||||
}
|
}
|
||||||
|
|
||||||
case "compress":
|
case "compress":
|
||||||
compressionFraming = .compress
|
compressionFraming = .compress
|
||||||
if $0.count > 1 {
|
if $0.count == 1 {
|
||||||
compressionAlgorithm = .other
|
|
||||||
} else {
|
|
||||||
compressionAlgorithm = .disabled
|
compressionAlgorithm = .disabled
|
||||||
|
} else if ($0.count == 2) && ($0[1] == "lzo") {
|
||||||
|
compressionAlgorithm = .LZO
|
||||||
|
} else {
|
||||||
|
compressionAlgorithm = .other
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -912,20 +912,20 @@ public class SessionProxy {
|
|||||||
reply = optionalReply
|
reply = optionalReply
|
||||||
log.debug("Received PUSH_REPLY: \"\(reply.maskedDescription)\"")
|
log.debug("Received PUSH_REPLY: \"\(reply.maskedDescription)\"")
|
||||||
|
|
||||||
if let framing = reply.compressionFraming, let compression = reply.compressionAlgorithm, compression != .disabled {
|
if let framing = reply.compressionFraming, let compression = reply.compressionAlgorithm {
|
||||||
switch framing {
|
switch compression {
|
||||||
case .compress:
|
case .disabled:
|
||||||
log.error("Server has new compression enabled and this is currently unsupported (\(framing))")
|
break
|
||||||
throw SessionError.serverCompression
|
|
||||||
|
|
||||||
case .compLZO:
|
case .LZO:
|
||||||
if !LZOIsSupported() {
|
if !LZOIsSupported() {
|
||||||
log.error("Server has legacy LZO compression enabled and this was not built into the library (\(framing))")
|
log.error("Server has LZO compression enabled and this was not built into the library (framing=\(framing))")
|
||||||
throw SessionError.serverCompression
|
throw SessionError.serverCompression
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
case .other:
|
||||||
break
|
log.error("Server has non-LZO compression enabled and this is currently unsupported (framing=\(framing))")
|
||||||
|
throw SessionError.serverCompression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch let e {
|
} catch let e {
|
||||||
|
Loading…
Reference in New Issue
Block a user