ServiceCom.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #import "ServiceCom.h"
  19. #import "AuthtokenCopy.h"
  20. #import "Network.h"
  21. #import "NodeStatus.h"
  22. @import AppKit;
  23. @interface ServiceCom (Private)
  24. - (NSString*)key;
  25. @end
  26. @implementation ServiceCom
  27. + (ServiceCom*)sharedInstance {
  28. static ServiceCom *sc = nil;
  29. static dispatch_once_t onceToken;
  30. dispatch_once(&onceToken, ^{
  31. sc = [[ServiceCom alloc] init];
  32. });
  33. return sc;
  34. }
  35. - (id)init
  36. {
  37. self = [super init];
  38. if(self) {
  39. baseURL = @"http://127.0.0.1:9993";
  40. session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
  41. _isQuitting = NO;
  42. _resetKey = NO;
  43. }
  44. return self;
  45. }
  46. - (NSString*)key:(NSError* __autoreleasing *)err
  47. {
  48. static NSString *k = nil;
  49. static NSUInteger resetCount = 0;
  50. @synchronized (self) {
  51. if (_isQuitting) {
  52. return @"";
  53. }
  54. if (_resetKey && k != nil) {
  55. k = nil;
  56. ++resetCount;
  57. NSLog(@"ResetCount: %lu", (unsigned long)resetCount);
  58. if (resetCount > 10) {
  59. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  60. NSAlert *alert = [NSAlert alertWithMessageText:@"Error obtaining Auth Token"
  61. defaultButton:@"Quit"
  62. alternateButton:@"Retry"
  63. otherButton:nil
  64. informativeTextWithFormat:@"Please ensure ZeroTier is installed correctly"];
  65. alert.alertStyle = NSCriticalAlertStyle;
  66. NSModalResponse res;
  67. if (!_isQuitting) {
  68. res = [alert runModal];
  69. }
  70. else {
  71. return;
  72. }
  73. if(res == 1) {
  74. _isQuitting = YES;
  75. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  76. }
  77. }];
  78. return @"";
  79. }
  80. }
  81. if (k == nil) {
  82. NSError *error = nil;
  83. NSURL *appSupportDir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:&error];
  84. if (error) {
  85. NSLog(@"Error: %@", error);
  86. return @"";
  87. }
  88. appSupportDir = [[appSupportDir URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"];
  89. NSURL *authtokenURL = [appSupportDir URLByAppendingPathComponent:@"authtoken.secret"];
  90. if (!_resetKey && [[NSFileManager defaultManager] fileExistsAtPath:[authtokenURL path]]) {
  91. k = [NSString stringWithContentsOfURL:authtokenURL
  92. encoding:NSUTF8StringEncoding
  93. error:&error];
  94. k = [k stringByReplacingOccurrencesOfString:@"\n" withString:@""];
  95. if (error) {
  96. NSLog(@"Error: %@", error);
  97. k = nil;
  98. *err = error;
  99. return @"";
  100. }
  101. }
  102. else {
  103. _resetKey = NO;
  104. NSURL *sysAppSupportDir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSSystemDomainMask appropriateForURL:nil create:false error:nil];
  105. sysAppSupportDir = [[sysAppSupportDir URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"];
  106. NSURL *sysAuthtokenURL = [sysAppSupportDir URLByAppendingPathComponent:@"authtoken.secret"];
  107. if(![[NSFileManager defaultManager] fileExistsAtPath:[sysAuthtokenURL path]]) {
  108. }
  109. [[NSFileManager defaultManager] createDirectoryAtURL:appSupportDir
  110. withIntermediateDirectories:YES
  111. attributes:nil
  112. error:&error];
  113. if (error) {
  114. NSLog(@"Error: %@", error);
  115. *err = error;
  116. k = nil;
  117. return @"";
  118. }
  119. AuthorizationRef authRef;
  120. OSStatus status = AuthorizationCreate(nil, nil, kAuthorizationFlagDefaults, &authRef);
  121. if (status != errAuthorizationSuccess) {
  122. NSLog(@"Authorization Failed! %d", status);
  123. NSDictionary *userInfo = @{
  124. NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't create AuthorizationRef", nil),
  125. };
  126. *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
  127. return @"";
  128. }
  129. AuthorizationItem authItem;
  130. authItem.name = kAuthorizationRightExecute;
  131. authItem.valueLength = 0;
  132. authItem.flags = 0;
  133. AuthorizationRights authRights;
  134. authRights.count = 1;
  135. authRights.items = &authItem;
  136. AuthorizationFlags authFlags = kAuthorizationFlagDefaults |
  137. kAuthorizationFlagInteractionAllowed |
  138. kAuthorizationFlagPreAuthorize |
  139. kAuthorizationFlagExtendRights;
  140. status = AuthorizationCopyRights(authRef, &authRights, nil, authFlags, nil);
  141. if (status != errAuthorizationSuccess) {
  142. NSLog(@"Authorization Failed! %d", status);
  143. NSDictionary *userInfo = @{
  144. NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't copy authorization rights", nil),
  145. };
  146. *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
  147. return @"";
  148. }
  149. NSString *localKey = getAdminAuthToken(authRef);
  150. AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
  151. if (localKey != nil && [localKey lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > 0) {
  152. k = localKey;
  153. [localKey writeToURL:authtokenURL
  154. atomically:YES
  155. encoding:NSUTF8StringEncoding
  156. error:&error];
  157. if (error) {
  158. NSLog(@"Error writing token to disk: %@", error);
  159. *err = error;
  160. }
  161. }
  162. }
  163. }
  164. if (k == nil) {
  165. NSDictionary *userInfo = @{
  166. NSLocalizedDescriptionKey: NSLocalizedString(@"Unknown error finding authorization key", nil),
  167. };
  168. *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
  169. return @"";
  170. }
  171. }
  172. return k;
  173. }
  174. - (void)getNetworklist:(void (^)(NSArray<Network *> *))completionHandler error:(NSError *__autoreleasing*)error
  175. {
  176. NSString* key = [self key:error];
  177. if(*error) {
  178. return;
  179. }
  180. NSString *urlString = [[baseURL stringByAppendingString:@"/network?auth="] stringByAppendingString:key];
  181. NSURL *url = [NSURL URLWithString:urlString];
  182. NSURLSessionDataTask *task =
  183. [session dataTaskWithURL:url
  184. completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  185. if (err) {
  186. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  187. NSAlert *alert = [NSAlert alertWithError:err];
  188. alert.alertStyle = NSCriticalAlertStyle;
  189. [alert addButtonWithTitle:@"Quit"];
  190. [alert addButtonWithTitle:@"Retry"];
  191. NSModalResponse res;
  192. if (!_isQuitting) {
  193. res = [alert runModal];
  194. }
  195. else {
  196. return;
  197. }
  198. if(res == NSAlertFirstButtonReturn) {
  199. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  200. _isQuitting = YES;
  201. }
  202. }];
  203. return;
  204. }
  205. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  206. NSInteger status = [httpResponse statusCode];
  207. NSError *err2;
  208. if (status == 200) {
  209. NSArray *json = [NSJSONSerialization JSONObjectWithData:data
  210. options:0
  211. error:&err2];
  212. if (err) {
  213. NSLog(@"Error fetching network list: %@", err2);
  214. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  215. NSAlert *alert = [NSAlert alertWithError:err2];
  216. alert.alertStyle = NSCriticalAlertStyle;
  217. [alert addButtonWithTitle:@"Quit"];
  218. [alert addButtonWithTitle:@"Retry"];
  219. NSModalResponse res;
  220. if (!_isQuitting) {
  221. res = [alert runModal];
  222. }
  223. else {
  224. return;
  225. }
  226. if(res == NSAlertFirstButtonReturn) {
  227. _isQuitting = YES;
  228. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  229. }
  230. }];
  231. return;
  232. }
  233. NSMutableArray<Network*> *networks = [[NSMutableArray<Network*> alloc] init];
  234. for(NSDictionary *dict in json) {
  235. [networks addObject:[[Network alloc] initWithJsonData:dict]];
  236. }
  237. completionHandler(networks);
  238. }
  239. else if (status == 401) {
  240. self->_resetKey = YES;
  241. }
  242. }];
  243. [task resume];
  244. }
  245. - (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler error:(NSError*__autoreleasing*)error
  246. {
  247. NSString *key = [self key:error];
  248. if(*error) {
  249. return;
  250. }
  251. NSString *urlString = [[baseURL stringByAppendingString:@"/status?auth="] stringByAppendingString:key];
  252. NSURL *url = [NSURL URLWithString:urlString];
  253. NSURLSessionDataTask *task =
  254. [session dataTaskWithURL:url
  255. completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  256. if(err) {
  257. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  258. NSAlert *alert = [NSAlert alertWithError:err];
  259. alert.alertStyle = NSCriticalAlertStyle;
  260. [alert addButtonWithTitle:@"Quit"];
  261. [alert addButtonWithTitle:@"Retry"];
  262. NSModalResponse res;
  263. if (!_isQuitting) {
  264. res = [alert runModal];
  265. }
  266. else {
  267. return;
  268. }
  269. if(res == NSAlertFirstButtonReturn) {
  270. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  271. _isQuitting = YES;
  272. }
  273. }];
  274. return;
  275. }
  276. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  277. NSInteger status = [httpResponse statusCode];
  278. NSError *err2;
  279. if(status == 200) {
  280. NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data
  281. options:0
  282. error:&err2];
  283. if(err2) {
  284. NSLog(@"Error fetching node status: %@", err2);
  285. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  286. NSAlert *alert = [NSAlert alertWithError:err2];
  287. alert.alertStyle = NSCriticalAlertStyle;
  288. [alert addButtonWithTitle:@"Quit"];
  289. [alert addButtonWithTitle:@"Retry"];
  290. NSModalResponse res;
  291. if (!_isQuitting) {
  292. res = [alert runModal];
  293. }
  294. else {
  295. return;
  296. }
  297. if(res == NSAlertFirstButtonReturn) {
  298. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  299. _isQuitting = YES;
  300. }
  301. }];
  302. return;
  303. }
  304. NodeStatus *status = [[NodeStatus alloc] initWithJsonData:json];
  305. completionHandler(status);
  306. }
  307. else if (status == 401) {
  308. self->_resetKey = YES;
  309. }
  310. }];
  311. [task resume];
  312. }
  313. - (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault error:(NSError *__autoreleasing*)error
  314. {
  315. NSString *key = [self key:error];
  316. if(*error) {
  317. return;
  318. }
  319. NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
  320. stringByAppendingString:networkId]
  321. stringByAppendingString:@"?auth="]
  322. stringByAppendingString:key];
  323. NSURL *url = [NSURL URLWithString:urlString];
  324. NSMutableDictionary *jsonDict = [NSMutableDictionary dictionary];
  325. [jsonDict setObject:[NSNumber numberWithBool:allowManaged] forKey:@"allowManaged"];
  326. [jsonDict setObject:[NSNumber numberWithBool:allowGlobal] forKey:@"allowGlobal"];
  327. [jsonDict setObject:[NSNumber numberWithBool:allowDefault] forKey:@"allowDefault"];
  328. NSError *err = nil;
  329. NSData *json = [NSJSONSerialization dataWithJSONObject:jsonDict
  330. options:0
  331. error:&err];
  332. if(err) {
  333. NSLog(@"Error creating json data: %@", err);
  334. *error = err;
  335. return;
  336. }
  337. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  338. request.HTTPMethod = @"POST";
  339. request.HTTPBody = json;
  340. [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  341. NSURLSessionDataTask *task =
  342. [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  343. if(err) {
  344. NSLog(@"Error posting join request: %@", err);
  345. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  346. NSAlert *alert = [NSAlert alertWithError:err];
  347. alert.alertStyle = NSCriticalAlertStyle;
  348. [alert addButtonWithTitle:@"Quit"];
  349. [alert addButtonWithTitle:@"Retry"];
  350. NSModalResponse res;
  351. if (!_isQuitting) {
  352. res = [alert runModal];
  353. }
  354. else {
  355. return;
  356. }
  357. if(res == NSAlertFirstButtonReturn) {
  358. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  359. _isQuitting = YES;
  360. }
  361. }];
  362. }
  363. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  364. NSInteger status = [httpResponse statusCode];
  365. if(status == 200) {
  366. NSLog(@"join ok");
  367. }
  368. else if (status == 401) {
  369. self->_resetKey = YES;
  370. }
  371. else {
  372. NSLog(@"join error: %ld", (long)status);
  373. }
  374. }];
  375. [task resume];
  376. }
  377. - (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error
  378. {
  379. NSString *key = [self key:error];
  380. if(*error) {
  381. return;
  382. }
  383. NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
  384. stringByAppendingString:networkId]
  385. stringByAppendingString:@"?auth="]
  386. stringByAppendingString:key];
  387. NSURL *url = [NSURL URLWithString:urlString];
  388. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  389. request.HTTPMethod = @"DELETE";
  390. NSURLSessionDataTask *task =
  391. [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  392. if(err) {
  393. NSLog(@"Error posting delete request: %@", err);
  394. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  395. NSAlert *alert = [NSAlert alertWithError:err];
  396. alert.alertStyle = NSCriticalAlertStyle;
  397. [alert addButtonWithTitle:@"Quit"];
  398. [alert addButtonWithTitle:@"Retry"];
  399. NSModalResponse res;
  400. if (!_isQuitting) {
  401. res = [alert runModal];
  402. }
  403. else {
  404. return;
  405. }
  406. if(res == NSAlertFirstButtonReturn) {
  407. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  408. _isQuitting = YES;
  409. }
  410. }];
  411. return;
  412. }
  413. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  414. NSInteger status = httpResponse.statusCode;
  415. if(status == 200) {
  416. NSLog(@"leave ok");
  417. }
  418. else if (status == 401) {
  419. self->_resetKey = YES;
  420. }
  421. else {
  422. NSLog(@"leave error: %ld", status);
  423. }
  424. }];
  425. [task resume];
  426. }
  427. @end