GCDAsyncSocketFactory.m 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2019-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "GCDAsyncSocketFactory.h"
  21. #import "GCDAsyncSocket.h"
  22. #import "GCDAsyncHTTPSProxySocket.h"
  23. #import "GCDAsyncSOCKSProxySocket.h"
  24. #import "ValidationLogger.h"
  25. #ifdef DEBUG
  26. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  27. #else
  28. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  29. #endif
  30. @implementation GCDAsyncSocketFactory
  31. static void _AutoConfigurationCallback(void *info, CFArrayRef proxyList, CFErrorRef error);
  32. struct AutoConfigLoadStatus {
  33. bool finished;
  34. CFArrayRef proxyList;
  35. };
  36. + (GCDAsyncSocket*)proxyAwareAsyncSocketForHost:(NSString*)host port:(NSNumber*)port delegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue {
  37. NSDictionary *systemProxies = (__bridge NSDictionary*)CFNetworkCopySystemProxySettings();
  38. NSURL *targetUrl = [NSURL URLWithString:[NSString stringWithFormat:@"https://%@:%@", host, port]];
  39. NSArray *urlProxies = (__bridge NSArray*)CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)targetUrl, (__bridge CFDictionaryRef _Nonnull)(systemProxies));
  40. return [GCDAsyncSocketFactory proxyAwareAsyncSocketForProxyList:urlProxies targetUrl:targetUrl delegate:delegate delegateQueue:delegateQueue];
  41. }
  42. + (GCDAsyncSocket*)proxyAwareAsyncSocketForProxyList:(NSArray*)proxyList targetUrl:(NSURL*)targetUrl delegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue {
  43. if (proxyList.count > 0) {
  44. NSDictionary *proxy = proxyList[0];
  45. if ([proxy[(NSString*)kCFProxyTypeKey] isEqualToString:(NSString*)kCFProxyTypeSOCKS]) {
  46. NSString *host = proxy[(NSString*)kCFProxyHostNameKey];
  47. NSNumber *port = proxy[(NSString*)kCFProxyPortNumberKey];
  48. NSString *username = proxy[(NSString*)kCFProxyUsernameKey];
  49. NSString *password = proxy[(NSString*)kCFProxyPasswordKey];
  50. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Using SOCKS proxy %@:%@", host, port]];
  51. GCDAsyncSOCKSProxySocket *proxySocket = [[GCDAsyncSOCKSProxySocket alloc] initWithDelegate:delegate delegateQueue:delegateQueue];
  52. [proxySocket setProxyHost:host port:port.intValue version:GCDAsyncSocketSOCKSVersion5];
  53. if (username && password) {
  54. [proxySocket setProxyUsername:username password:password];
  55. }
  56. return proxySocket;
  57. } else if ([proxy[(NSString*)kCFProxyTypeKey] isEqualToString:(NSString*)kCFProxyTypeHTTPS]) {
  58. NSString *host = proxy[(NSString*)kCFProxyHostNameKey];
  59. NSNumber *port = proxy[(NSString*)kCFProxyPortNumberKey];
  60. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Using HTTPS proxy %@:%@", host, port]];
  61. GCDAsyncHTTPSProxySocket *proxySocket = [[GCDAsyncHTTPSProxySocket alloc] initWithDelegate:delegate delegateQueue:delegateQueue];
  62. [proxySocket setProxyHost:host port:port.intValue];
  63. return proxySocket;
  64. } else if ([proxy[(NSString*)kCFProxyTypeKey] isEqualToString:(NSString*)kCFProxyTypeAutoConfigurationURL]) {
  65. NSURL *autoConfigUrl = proxy[(NSString*)kCFProxyAutoConfigurationURLKey];
  66. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Loading proxy auto config from %@", autoConfigUrl]];
  67. struct AutoConfigLoadStatus status;
  68. status.finished = false;
  69. CFStreamClientContext ctxt = { 0, &status, NULL, NULL, NULL };
  70. CFRunLoopSourceRef rls = CFNetworkExecuteProxyAutoConfigurationURL((__bridge CFURLRef _Nonnull)autoConfigUrl, (__bridge CFURLRef _Nonnull)(targetUrl), _AutoConfigurationCallback, &ctxt);
  71. CFStringRef mode = CFSTR("__GCDProxyAutoConfigRunLoopMode");
  72. CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, mode);
  73. CFAbsoluteTime stopTime = CFAbsoluteTimeGetCurrent() + 10.0;
  74. do {
  75. (void) CFRunLoopRunInMode(mode, 0.1, TRUE);
  76. } while (!status.finished && CFAbsoluteTimeGetCurrent() < stopTime);
  77. if (status.finished && status.proxyList) {
  78. DDLogVerbose(@"Auto config proxy list: %@", status.proxyList);
  79. return [GCDAsyncSocketFactory proxyAwareAsyncSocketForProxyList:(__bridge NSArray *)(status.proxyList) targetUrl:targetUrl delegate:delegate delegateQueue:delegateQueue];
  80. }
  81. if (rls) {
  82. if (CFRunLoopSourceIsValid(rls)) {
  83. CFRunLoopSourceInvalidate(rls);
  84. CFRelease(rls);
  85. }
  86. }
  87. }
  88. }
  89. return [[GCDAsyncSocket alloc] initWithDelegate:delegate delegateQueue:delegateQueue];
  90. }
  91. static void _AutoConfigurationCallback(void *info, CFArrayRef proxyList, CFErrorRef error) {
  92. struct AutoConfigLoadStatus *status = info;
  93. status->proxyList = proxyList;
  94. status->finished = true;
  95. }
  96. @end