Merge pull request #70 from keeshux/support-compress-lzo

Support --compress lzo
This commit is contained in:
Davide De Rosa 2019-03-20 16:45:21 +01:00 committed by GitHub
commit 76631a00fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 70 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- 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)

View File

@ -29,11 +29,10 @@ The client is known to work with [OpenVPN®][openvpn] 2.3+ servers.
- Authentication (`--tls-auth`)
- Encryption (`--tls-crypt`)
- [x] Compression framing
- Disabled
- Compress (2.4)
- LZO (deprecated in 2.4)
- Via `--comp-lzo` (deprecated in 2.4)
- Via `--compress`
- [x] Compression algorithms
- LZO (`--comp-lzo` only)
- LZO (via `--comp-lzo` or `--compress lzo`)
- [x] Key renegotiation
- [x] Replay protection (hardcoded window)
@ -46,8 +45,7 @@ TunnelKit can parse .ovpn configuration files. Below are a few limitations worth
Unsupported:
- UDP fragmentation, i.e. `--fragment`
- Compression
- `--compress` other than empty
- Compression via `--compress` other than empty or `lzo`
- Proxy
- External file references (inline `<block>` only)
- Encrypted client certificate keys

View File

@ -37,9 +37,9 @@ Pod::Spec.new do |s|
s.subspec "LZO" do |p|
p.source_files = "TunnelKit/Sources/Core/LZO.h",
"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",
"TunnelKit/Sources/LZO/lib/*.h"
"TunnelKit/Sources/LZO/lib/*lzo*.h"
p.pod_target_xcconfig = { "APPLICATION_EXTENSION_API_ONLY" => "YES" }
end
end

View File

@ -315,10 +315,17 @@ public class ConfigurationParser {
isHandled = true
compressionFraming = .compress
guard $0.isEmpty else {
compressionAlgorithm = .other
unsupportedError = .unsupportedConfiguration(option: line)
return
if !LZOIsSupported() {
guard $0.isEmpty else {
unsupportedError = .unsupportedConfiguration(option: line)
return
}
} else {
if let arg = $0.first {
compressionAlgorithm = (arg == "lzo") ? .LZO : .other
} else {
compressionAlgorithm = .disabled
}
}
}
Regex.keyDirection.enumerateArguments(in: line) {

View File

@ -117,7 +117,7 @@
[self.decrypter setPeerId:peerId];
[self setCompressionFraming:compressionFraming];
if (LZOIsSupported() && (compressionFraming == CompressionFramingNativeCompLZO) && (compressionAlgorithm == CompressionAlgorithmNativeLZO)) {
if (LZOIsSupported() && (compressionAlgorithm == CompressionAlgorithmNativeLZO)) {
self.lzo = LZOCreate();
}
}
@ -168,6 +168,42 @@
- (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) {
case CompressionFramingNativeDisabled: {
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
@ -184,27 +220,30 @@
}
case CompressionFramingNativeCompress: {
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
memcpy(packetDest, payload.bytes, payload.length);
packetDest[payload.length] = packetDest[0];
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;
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);
packetDest[payload.length] = packetDest[0];
packetDest[0] = DataPacketNoCompressSwap;
}
}
payload[0] = packet[packetLength - 1];
*payloadOffset = 0;
*headerLength = 1;
return YES;
};
self.parsePayloadBlock = parseCompressedBlock;
break;
}
case CompressionFramingNativeCompLZO: {
__weak DataPath *weakSelf = self;
self.assemblePayloadBlock = ^(uint8_t * packetDest, NSInteger * packetLengthOffset, NSData * payload) {
NSData *compressedPayload = [weakSelf.lzo compressedDataWithData:payload error:NULL];
if (compressedPayload) {
@ -217,32 +256,7 @@
}
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) {
*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;
};
self.parsePayloadBlock = parseCompressedBlock;
break;
}
}

View File

@ -395,18 +395,20 @@ extension SessionProxy {
switch $0[0] {
case "comp-lzo":
compressionFraming = .compLZO
if !(($0.count == 2) && ($0[1] == "no")) {
compressionAlgorithm = .LZO
} else {
if ($0.count == 2) && ($0[1] == "no") {
compressionAlgorithm = .disabled
} else {
compressionAlgorithm = .LZO
}
case "compress":
compressionFraming = .compress
if $0.count > 1 {
compressionAlgorithm = .other
} else {
if $0.count == 1 {
compressionAlgorithm = .disabled
} else if ($0.count == 2) && ($0[1] == "lzo") {
compressionAlgorithm = .LZO
} else {
compressionAlgorithm = .other
}
default:

View File

@ -912,20 +912,20 @@ public class SessionProxy {
reply = optionalReply
log.debug("Received PUSH_REPLY: \"\(reply.maskedDescription)\"")
if let framing = reply.compressionFraming, let compression = reply.compressionAlgorithm, compression != .disabled {
switch framing {
case .compress:
log.error("Server has new compression enabled and this is currently unsupported (\(framing))")
throw SessionError.serverCompression
if let framing = reply.compressionFraming, let compression = reply.compressionAlgorithm {
switch compression {
case .disabled:
break
case .compLZO:
case .LZO:
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
}
default:
break
case .other:
log.error("Server has non-LZO compression enabled and this is currently unsupported (framing=\(framing))")
throw SessionError.serverCompression
}
}
} catch let e {