// // WireGuardGoWrapper.m // WireGuardNetworkExtension // // Created by Jeroen Leenarts on 21-06-18. // Copyright © 2018 Jason A. Donenfeld . All rights reserved. // #include #include "wireguard.h" #import "WireGuardGoWrapper.h" /// Trampoline function static ssize_t do_read(const void *ctx, const unsigned char *buf, size_t len); /// Trampoline function static ssize_t do_write(const void *ctx, const unsigned char *buf, size_t len); /// Trampoline function static void do_log(int level, const char *tag, const char *msg); @interface WireGuardGoWrapper () @property (nonatomic, assign) int handle; @property (nonatomic, assign) BOOL isClosed; @property (nonatomic, strong) NSMutableArray *packets; @property (nonatomic, strong) NSMutableArray *protocols; @property (nonatomic, strong) NSCondition *condition; @end @implementation WireGuardGoWrapper - (instancetype)init { self = [super init]; if (self) { self.condition = [NSCondition new]; } return self; } - (BOOL) turnOnWithInterfaceName: (NSString *)interfaceName settingsString: (NSString *)settingsString { os_log([WireGuardGoWrapper log], "WireGuard Go Version %{public}s", wgVersion()); wgSetLogger(do_log); const char * ifName = [interfaceName UTF8String]; const char * settings = [settingsString UTF8String]; self.handle = wgTurnOn((gostring_t){ .p = ifName, .n = interfaceName.length }, (gostring_t){ .p = settings, .n = settingsString.length }, do_read, do_write, (__bridge void *)(self)); return self.handle > 0; } - (void) turnOff { self.isClosed = YES; wgTurnOff(self.handle); } + (NSString *)versionWireGuardGo { return [NSString stringWithUTF8String:wgVersion()]; } + (os_log_t)log { static os_log_t subLog = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ subLog = os_log_create("com.wireguard.ios.WireGuard.WireGuardNetworkExtension", "WireGuard-Go"); }); return subLog; } @end static ssize_t do_read(const void *ctx, const unsigned char *buf, size_t len) { WireGuardGoWrapper *wrapper = (__bridge WireGuardGoWrapper *)ctx; if (wrapper.isClosed) return -1; if (wrapper.packets.count == 0) { [wrapper.packetFlow readPacketsWithCompletionHandler:^(NSArray * _Nonnull packets, NSArray * _Nonnull protocols) { [wrapper.packets addObjectsFromArray:packets]; [wrapper.protocols addObjectsFromArray:protocols]; // TODO make sure that the completion handler and the do_read are not performed on the same thread. [wrapper.condition signal]; }]; [wrapper.condition wait]; } NSData *packet = [wrapper.packets objectAtIndex:0]; // NSNumber *protocol = [wrapper.protocols objectAtIndex:0]; [wrapper.packets removeObjectAtIndex:0]; [wrapper.protocols removeObjectAtIndex:0]; NSUInteger packetLength = [packet length]; if (packetLength > len) { // The packet will be dropped when we end up here. return 0; } memcpy(buf, [packet bytes], packetLength); return packetLength; } static ssize_t do_write(const void *ctx, const unsigned char *buf, size_t len) { WireGuardGoWrapper *wrapper = (__bridge WireGuardGoWrapper *)ctx; //TODO: determine IPv4 or IPv6 status. NSData *packet = [[NSData alloc] initWithBytes:buf length:len]; [wrapper.packetFlow writePackets:@[packet] withProtocols:@[@AF_INET]]; return len; } static void do_log(int level, const char *tag, const char *msg) { // TODO Get some details on the log level and distribute to matching log levels. os_log([WireGuardGoWrapper log], "Log level %d for %{public}s: %{public}s", level, tag, msg); }