ServiceCom.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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
  314. allowManaged:(BOOL)allowManaged
  315. allowGlobal:(BOOL)allowGlobal
  316. allowDefault:(BOOL)allowDefault
  317. allowDNS:(BOOL)allowDNS
  318. error:(NSError *__autoreleasing*)error
  319. {
  320. NSString *key = [self key:error];
  321. if(*error) {
  322. return;
  323. }
  324. NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
  325. stringByAppendingString:networkId]
  326. stringByAppendingString:@"?auth="]
  327. stringByAppendingString:key];
  328. NSURL *url = [NSURL URLWithString:urlString];
  329. NSMutableDictionary *jsonDict = [NSMutableDictionary dictionary];
  330. [jsonDict setObject:[NSNumber numberWithBool:allowManaged] forKey:@"allowManaged"];
  331. [jsonDict setObject:[NSNumber numberWithBool:allowGlobal] forKey:@"allowGlobal"];
  332. [jsonDict setObject:[NSNumber numberWithBool:allowDefault] forKey:@"allowDefault"];
  333. [jsonDict setObject:[NSNumber numberWithBool:allowDNS] forKey:@"allowDNS"];
  334. NSError *err = nil;
  335. NSData *json = [NSJSONSerialization dataWithJSONObject:jsonDict
  336. options:0
  337. error:&err];
  338. if(err) {
  339. NSLog(@"Error creating json data: %@", err);
  340. *error = err;
  341. return;
  342. }
  343. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  344. request.HTTPMethod = @"POST";
  345. request.HTTPBody = json;
  346. [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  347. NSURLSessionDataTask *task =
  348. [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  349. if(err) {
  350. NSLog(@"Error posting join request: %@", err);
  351. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  352. NSAlert *alert = [NSAlert alertWithError:err];
  353. alert.alertStyle = NSCriticalAlertStyle;
  354. [alert addButtonWithTitle:@"Quit"];
  355. [alert addButtonWithTitle:@"Retry"];
  356. NSModalResponse res;
  357. if (!_isQuitting) {
  358. res = [alert runModal];
  359. }
  360. else {
  361. return;
  362. }
  363. if(res == NSAlertFirstButtonReturn) {
  364. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  365. _isQuitting = YES;
  366. }
  367. }];
  368. }
  369. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  370. NSInteger status = [httpResponse statusCode];
  371. if(status == 200) {
  372. NSLog(@"join ok");
  373. }
  374. else if (status == 401) {
  375. self->_resetKey = YES;
  376. }
  377. else {
  378. NSLog(@"join error: %ld", (long)status);
  379. }
  380. }];
  381. [task resume];
  382. }
  383. - (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error
  384. {
  385. NSString *key = [self key:error];
  386. if(*error) {
  387. return;
  388. }
  389. NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
  390. stringByAppendingString:networkId]
  391. stringByAppendingString:@"?auth="]
  392. stringByAppendingString:key];
  393. NSURL *url = [NSURL URLWithString:urlString];
  394. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  395. request.HTTPMethod = @"DELETE";
  396. NSURLSessionDataTask *task =
  397. [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
  398. if(err) {
  399. NSLog(@"Error posting delete request: %@", err);
  400. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  401. NSAlert *alert = [NSAlert alertWithError:err];
  402. alert.alertStyle = NSCriticalAlertStyle;
  403. [alert addButtonWithTitle:@"Quit"];
  404. [alert addButtonWithTitle:@"Retry"];
  405. NSModalResponse res;
  406. if (!_isQuitting) {
  407. res = [alert runModal];
  408. }
  409. else {
  410. return;
  411. }
  412. if(res == NSAlertFirstButtonReturn) {
  413. [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
  414. _isQuitting = YES;
  415. }
  416. }];
  417. return;
  418. }
  419. NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
  420. NSInteger status = httpResponse.statusCode;
  421. if(status == 200) {
  422. NSLog(@"leave ok");
  423. }
  424. else if (status == 401) {
  425. self->_resetKey = YES;
  426. }
  427. else {
  428. NSLog(@"leave error: %ld", status);
  429. }
  430. }];
  431. [task resume];
  432. }
  433. @end