GCDAsyncSOCKSProxySocket.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. //
  2. // GCDAsyncSOCKSProxySocket.m
  3. //
  4. // Copyright © 2019 Threema GmbH. All rights reserved.
  5. // Derived from ProxyKit, Copyright (c) 2014 Chris Ballinger
  6. //
  7. #import "GCDAsyncSOCKSProxySocket.h"
  8. #if DEBUG
  9. static const int ddLogLevel = DDLogLevelVerbose;
  10. #else
  11. static const int ddLogLevel = DDLogLevelOff;
  12. #endif
  13. // Define various socket tags
  14. #define SOCKS_OPEN 10100
  15. #define SOCKS_CONNECT 10200
  16. #define SOCKS_CONNECT_REPLY_1 10300
  17. #define SOCKS_CONNECT_REPLY_2 10400
  18. #define SOCKS_AUTH_USERPASS 10500
  19. // Timeouts
  20. #define TIMEOUT_CONNECT 8.00
  21. #define TIMEOUT_READ 5.00
  22. #define TIMEOUT_TOTAL 80.00
  23. @interface GCDAsyncSOCKSProxySocket()
  24. @property (nonatomic, strong, readonly) GCDAsyncSocket *proxySocket;
  25. @property (nonatomic, readonly) dispatch_queue_t proxyDelegateQueue;
  26. @property (nonatomic, strong, readonly) NSString *destinationHost;
  27. @property (nonatomic, readonly) uint16_t destinationPort;
  28. @property (nonatomic, strong) NSError *lastDisconnectError;
  29. @end
  30. @implementation GCDAsyncSOCKSProxySocket
  31. - (void) setProxyHost:(NSString *)host port:(uint16_t)port version:(GCDAsyncSocketSOCKSVersion)version {
  32. _proxyHost = host;
  33. _proxyPort = port;
  34. _proxyVersion = version;
  35. }
  36. - (void) setProxyUsername:(NSString *)username password:(NSString *)password {
  37. _proxyUsername = username;
  38. _proxyPassword = password;
  39. }
  40. #pragma mark Overridden methods
  41. - (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq {
  42. if (self = [super initWithDelegate:aDelegate delegateQueue:dq socketQueue:sq]) {
  43. _proxyHost = nil;
  44. _proxyPort = 0;
  45. _proxyVersion = -1;
  46. _destinationHost = nil;
  47. _destinationPort = 0;
  48. _proxyUsername = nil;
  49. _proxyPassword = nil;
  50. _proxyDelegateQueue = dispatch_queue_create("GCDAsyncSOCKSProxySocket delegate queue", 0);
  51. }
  52. return self;
  53. }
  54. - (BOOL)connectToHost:(NSString *)inHost
  55. onPort:(uint16_t)port
  56. viaInterface:(NSString *)inInterface
  57. withTimeout:(NSTimeInterval)timeout
  58. error:(NSError **)errPtr
  59. {
  60. if (!self.proxySocket) {
  61. _proxySocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.proxyDelegateQueue socketQueue:NULL];
  62. }
  63. _destinationHost = inHost;
  64. _destinationPort = port;
  65. return [self.proxySocket connectToHost:self.proxyHost onPort:self.proxyPort viaInterface:inInterface withTimeout:timeout error:errPtr];
  66. }
  67. /** Returns YES if tag is reserved for internal functions */
  68. - (BOOL) checkForReservedTag:(long)tag {
  69. if (tag == SOCKS_OPEN || tag == SOCKS_CONNECT || tag == SOCKS_CONNECT_REPLY_1 || tag == SOCKS_CONNECT_REPLY_2 || tag == SOCKS_AUTH_USERPASS) {
  70. return YES;
  71. } else {
  72. return NO;
  73. }
  74. }
  75. - (void) writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag {
  76. if ([self checkForReservedTag:tag]) {
  77. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  78. return;
  79. }
  80. [self.proxySocket writeData:data withTimeout:timeout tag:tag];
  81. }
  82. - (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset tag:(long)tag {
  83. if ([self checkForReservedTag:tag]) {
  84. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  85. return;
  86. }
  87. [self.proxySocket readDataWithTimeout:timeout buffer:buffer bufferOffset:offset tag:tag];
  88. }
  89. - (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset maxLength:(NSUInteger)length tag:(long)tag {
  90. if ([self checkForReservedTag:tag]) {
  91. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  92. return;
  93. }
  94. [self.proxySocket readDataWithTimeout:timeout buffer:buffer bufferOffset:offset maxLength:length tag:tag];
  95. }
  96. - (void) readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag {
  97. if ([self checkForReservedTag:tag]) {
  98. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  99. return;
  100. }
  101. [self.proxySocket readDataWithTimeout:timeout tag:tag];
  102. }
  103. - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag {
  104. if ([self checkForReservedTag:tag]) {
  105. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  106. return;
  107. }
  108. [self.proxySocket readDataToLength:length withTimeout:timeout tag:tag];
  109. }
  110. - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset tag:(long)tag {
  111. if ([self checkForReservedTag:tag]) {
  112. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  113. return;
  114. }
  115. [self.proxySocket readDataToLength:length withTimeout:timeout buffer:buffer bufferOffset:offset tag:tag];
  116. }
  117. - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag {
  118. if ([self checkForReservedTag:tag]) {
  119. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  120. return;
  121. }
  122. [self.proxySocket readDataToData:data withTimeout:timeout tag:tag];
  123. }
  124. - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset tag:(long)tag {
  125. if ([self checkForReservedTag:tag]) {
  126. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  127. return;
  128. }
  129. [self.proxySocket readDataToData:data withTimeout:timeout buffer:buffer bufferOffset:offset tag:tag];
  130. }
  131. - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(NSUInteger)length tag:(long)tag {
  132. if ([self checkForReservedTag:tag]) {
  133. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  134. return;
  135. }
  136. [self.proxySocket readDataToData:data withTimeout:timeout maxLength:length tag:tag];
  137. }
  138. - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset maxLength:(NSUInteger)length tag:(long)tag {
  139. if ([self checkForReservedTag:tag]) {
  140. DDLogError(@"This tag is reserved and won't work: %ld", tag);
  141. return;
  142. }
  143. [self.proxySocket readDataToData:data withTimeout:timeout buffer:buffer bufferOffset:offset maxLength:length tag:tag];
  144. }
  145. - (void) startTLS:(NSDictionary *)tlsSettings {
  146. NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithDictionary:tlsSettings];
  147. /*
  148. NSString *peerName = self.destinationHost;
  149. [settings setObject:peerName forKey:(NSString *)kCFStreamSSLPeerName];
  150. */
  151. [self.proxySocket startTLS:settings];
  152. }
  153. - (void) disconnect {
  154. self.lastDisconnectError = nil;
  155. [self.proxySocket disconnect];
  156. }
  157. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  158. #pragma mark SOCKS
  159. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  160. /**
  161. * Sends the SOCKS5 open/handshake/authentication data, and starts reading the response.
  162. * We attempt to gain anonymous access (no authentication).
  163. **/
  164. - (void)socksOpen
  165. {
  166. // +-----+-----------+---------+
  167. // NAME | VER | NMETHODS | METHODS |
  168. // +-----+-----------+---------+
  169. // SIZE | 1 | 1 | 1 - 255 |
  170. // +-----+-----------+---------+
  171. //
  172. // Note: Size is in bytes
  173. //
  174. // Version = 5 (for SOCKS5)
  175. // NumMethods = 1
  176. // Method = 0 (No authentication, anonymous access)
  177. NSUInteger byteBufferLength = 3;
  178. uint8_t *byteBuffer = malloc(byteBufferLength * sizeof(uint8_t));
  179. uint8_t version = 5; // VER
  180. byteBuffer[0] = version;
  181. uint8_t numMethods = 1; // NMETHODS
  182. byteBuffer[1] = numMethods;
  183. uint8_t method = 0; // 0 == no auth
  184. if (self.proxyUsername.length || self.proxyPassword.length) {
  185. method = 2; // username/password
  186. }
  187. byteBuffer[2] = method;
  188. NSData *data = [NSData dataWithBytesNoCopy:byteBuffer length:byteBufferLength freeWhenDone:YES];
  189. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_OPEN: %@", data);
  190. [self.proxySocket writeData:data withTimeout:-1 tag:SOCKS_OPEN];
  191. // +-----+--------+
  192. // NAME | VER | METHOD |
  193. // +-----+--------+
  194. // SIZE | 1 | 1 |
  195. // +-----+--------+
  196. //
  197. // Note: Size is in bytes
  198. //
  199. // Version = 5 (for SOCKS5)
  200. // Method = 0 (No authentication, anonymous access)
  201. [self.proxySocket readDataToLength:2 withTimeout:TIMEOUT_READ tag:SOCKS_OPEN];
  202. }
  203. /*
  204. For username/password authentication the client's authentication request is
  205. field 1: version number, 1 byte (must be 0x01)
  206. field 2: username length, 1 byte
  207. field 3: username
  208. field 4: password length, 1 byte
  209. field 5: password
  210. */
  211. - (void)socksUserPassAuth {
  212. NSData *usernameData = [self.proxyUsername dataUsingEncoding:NSUTF8StringEncoding];
  213. NSData *passwordData = [self.proxyPassword dataUsingEncoding:NSUTF8StringEncoding];
  214. uint8_t usernameLength = (uint8_t)usernameData.length;
  215. uint8_t passwordLength = (uint8_t)passwordData.length;
  216. NSMutableData *authData = [NSMutableData dataWithCapacity:1+1+usernameLength+1+passwordLength];
  217. uint8_t version[1] = {0x01};
  218. [authData appendBytes:version length:1];
  219. [authData appendBytes:&usernameLength length:1];
  220. [authData appendBytes:usernameData.bytes length:usernameLength];
  221. [authData appendBytes:&passwordLength length:1];
  222. [authData appendBytes:passwordData.bytes length:passwordLength];
  223. [self.proxySocket writeData:authData withTimeout:-1 tag:SOCKS_AUTH_USERPASS];
  224. [self.proxySocket readDataToLength:2 withTimeout:-1 tag:SOCKS_AUTH_USERPASS];
  225. }
  226. /**
  227. * Sends the SOCKS5 connect data (according to XEP-65), and starts reading the response.
  228. **/
  229. - (void)socksConnect
  230. {
  231. // +-----+-----+-----+------+------+------+
  232. // NAME | VER | CMD | RSV | ATYP | ADDR | PORT |
  233. // +-----+-----+-----+------+------+------+
  234. // SIZE | 1 | 1 | 1 | 1 | var | 2 |
  235. // +-----+-----+-----+------+------+------+
  236. //
  237. // Note: Size is in bytes
  238. //
  239. // Version = 5 (for SOCKS5)
  240. // Command = 1 (for Connect)
  241. // Reserved = 0
  242. // Address Type = 3 (1=IPv4, 3=DomainName 4=IPv6)
  243. // Address = P:D (P=LengthOfDomain D=DomainWithoutNullTermination)
  244. // Port = 0
  245. NSUInteger hostLength = [self.destinationHost length];
  246. NSData *hostData = [self.destinationHost dataUsingEncoding:NSUTF8StringEncoding];
  247. NSUInteger byteBufferLength = (uint)(4 + 1 + hostLength + 2);
  248. uint8_t *byteBuffer = malloc(byteBufferLength * sizeof(uint8_t));
  249. NSUInteger offset = 0;
  250. // VER
  251. uint8_t version = 0x05;
  252. byteBuffer[0] = version;
  253. offset++;
  254. /* CMD
  255. o CONNECT X'01'
  256. o BIND X'02'
  257. o UDP ASSOCIATE X'03'
  258. */
  259. uint8_t command = 0x01;
  260. byteBuffer[offset] = command;
  261. offset++;
  262. byteBuffer[offset] = 0x00; // Reserved, must be 0
  263. offset++;
  264. /* ATYP
  265. o IP V4 address: X'01'
  266. o DOMAINNAME: X'03'
  267. o IP V6 address: X'04'
  268. */
  269. uint8_t addressType = 0x03;
  270. byteBuffer[offset] = addressType;
  271. offset++;
  272. /* ADDR
  273. o X'01' - the address is a version-4 IP address, with a length of 4 octets
  274. o X'03' - the address field contains a fully-qualified domain name. The first
  275. octet of the address field contains the number of octets of name that
  276. follow, there is no terminating NUL octet.
  277. o X'04' - the address is a version-6 IP address, with a length of 16 octets.
  278. */
  279. byteBuffer[offset] = hostLength;
  280. offset++;
  281. memcpy(byteBuffer+offset, [hostData bytes], hostLength);
  282. offset+=hostLength;
  283. uint16_t port = htons(self.destinationPort);
  284. NSUInteger portLength = 2;
  285. memcpy(byteBuffer+offset, &port, portLength);
  286. offset+=portLength;
  287. NSData *data = [NSData dataWithBytesNoCopy:byteBuffer length:byteBufferLength freeWhenDone:YES];
  288. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_CONNECT: %@", data);
  289. [self.proxySocket writeData:data withTimeout:-1 tag:SOCKS_CONNECT];
  290. // +-----+-----+-----+------+------+------+
  291. // NAME | VER | REP | RSV | ATYP | ADDR | PORT |
  292. // +-----+-----+-----+------+------+------+
  293. // SIZE | 1 | 1 | 1 | 1 | var | 2 |
  294. // +-----+-----+-----+------+------+------+
  295. //
  296. // Note: Size is in bytes
  297. //
  298. // Version = 5 (for SOCKS5)
  299. // Reply = 0 (0=Succeeded, X=ErrorCode)
  300. // Reserved = 0
  301. // Address Type = 3 (1=IPv4, 3=DomainName 4=IPv6)
  302. // Address = P:D (P=LengthOfDomain D=DomainWithoutNullTermination)
  303. // Port = 0
  304. //
  305. // It is expected that the SOCKS server will return the same address given in the connect request.
  306. // But according to XEP-65 this is only marked as a SHOULD and not a MUST.
  307. // So just in case, we'll read up to the address length now, and then read in the address+port next.
  308. [self.proxySocket readDataToLength:5 withTimeout:TIMEOUT_READ tag:SOCKS_CONNECT_REPLY_1];
  309. }
  310. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  311. #pragma mark AsyncSocket Delegate Methods
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  313. - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
  314. {
  315. DDLogInfo(@"proxySocket did connect to %@:%d", host, port);
  316. // Start the SOCKS protocol stuff
  317. [self socksOpen];
  318. }
  319. - (void) socket:(GCDAsyncSocket *)sock didReadPartialDataOfLength:(NSUInteger)partialLength tag:(long)tag {
  320. DDLogVerbose(@"read partial data with tag %ld of length %d", tag, (int)partialLength);
  321. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didReadPartialDataOfLength:tag:)]) {
  322. dispatch_async(self.delegateQueue, ^{
  323. @autoreleasepool {
  324. [self.delegate socket:self didReadPartialDataOfLength:partialLength tag:tag];
  325. }
  326. });
  327. }
  328. }
  329. - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
  330. {
  331. DDLogVerbose(@"did read tag[%ld] data: %@", tag, data);
  332. if (tag == SOCKS_OPEN)
  333. {
  334. NSAssert(data.length == 2, @"SOCKS_OPEN reply length must be 2!");
  335. // See socksOpen method for socks reply format
  336. uint8_t *bytes = (uint8_t*)[data bytes];
  337. uint8_t version = bytes[0];
  338. uint8_t method = bytes[1];
  339. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_OPEN: ver(%o) mtd(%o)", version, method);
  340. if(version == 5)
  341. {
  342. if (method == 0) { // No Auth
  343. [self socksConnect];
  344. } else if (method == 2) { // Username / password
  345. [self socksUserPassAuth];
  346. } else {
  347. // unsupported auth method
  348. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:100 userInfo:@{NSLocalizedDescriptionKey: @"Unsupported SOCKS auth method"}];
  349. [self.proxySocket disconnect];
  350. }
  351. }
  352. else
  353. {
  354. // Wrong version
  355. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:101 userInfo:@{NSLocalizedDescriptionKey: @"Wrong SOCKS version"}];
  356. [self.proxySocket disconnect];
  357. }
  358. }
  359. else if (tag == SOCKS_CONNECT_REPLY_1)
  360. {
  361. // See socksConnect method for socks reply format
  362. NSAssert(data.length == 5, @"SOCKS_CONNECT_REPLY_1 length must be 5!");
  363. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_CONNECT_REPLY_1: %@", data);
  364. uint8_t *bytes = (uint8_t*)[data bytes];
  365. uint8_t ver = bytes[0];
  366. uint8_t rep = bytes[1];
  367. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_CONNECT_REPLY_1: ver(%o) rep(%o)", ver, rep);
  368. if(ver == 5 && rep == 0)
  369. {
  370. // We read in 5 bytes which we expect to be:
  371. // 0: ver = 5
  372. // 1: rep = 0
  373. // 2: rsv = 0
  374. // 3: atyp = 3
  375. // 4: size = size of addr field
  376. //
  377. // However, some servers don't follow the protocol, and send a atyp value of 0.
  378. uint8_t addressType = bytes[3];
  379. uint8_t portLength = 2;
  380. if (addressType == 1) { // IPv4
  381. // only need to read 3 address bytes instead of 4 + portlength because we read an extra byte already
  382. [self.proxySocket readDataToLength:(3+portLength) withTimeout:TIMEOUT_READ tag:SOCKS_CONNECT_REPLY_2];
  383. }
  384. else if (addressType == 3) // Domain name
  385. {
  386. uint8_t addrLength = bytes[4];
  387. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: addrLength: %o", addrLength);
  388. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: portLength: %o", portLength);
  389. [self.proxySocket readDataToLength:(addrLength+portLength)
  390. withTimeout:TIMEOUT_READ
  391. tag:SOCKS_CONNECT_REPLY_2];
  392. } else if (addressType == 4) { // IPv6
  393. [self.proxySocket readDataToLength:(16+portLength) withTimeout:TIMEOUT_READ tag:SOCKS_CONNECT_REPLY_2];
  394. } else if (addressType == 0) {
  395. // The size field was actually the first byte of the port field
  396. // We just have to read in that last byte
  397. [self.proxySocket readDataToLength:1 withTimeout:TIMEOUT_READ tag:SOCKS_CONNECT_REPLY_2];
  398. } else {
  399. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: Unknown atyp field in connect reply");
  400. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:102 userInfo:@{NSLocalizedDescriptionKey: @"Unknown atyp field in connect reply"}];
  401. [self.proxySocket disconnect];
  402. }
  403. }
  404. else
  405. {
  406. NSString *failureReason = nil;
  407. switch (rep) {
  408. case 1:
  409. failureReason = @"general SOCKS server failure";
  410. break;
  411. case 2:
  412. failureReason = @"connection not allowed by ruleset";
  413. break;
  414. case 3:
  415. failureReason = @"Network unreachable";
  416. break;
  417. case 4:
  418. failureReason = @"Host unreachable";
  419. break;
  420. case 5:
  421. failureReason = @"Connection refused";
  422. break;
  423. case 6:
  424. failureReason = @"TTL expired";
  425. break;
  426. case 7:
  427. failureReason = @"Command not supported";
  428. break;
  429. case 8:
  430. failureReason = @"Address type not supported";
  431. break;
  432. default: // X'09' to X'FF' unassigned
  433. failureReason = @"unknown socks error";
  434. break;
  435. }
  436. DDLogVerbose(@"SOCKS failed, disconnecting: %@", failureReason);
  437. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:103 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"SOCKS failure: %@", failureReason]}];
  438. // Some kind of error occurred.
  439. [self.proxySocket disconnect];
  440. }
  441. }
  442. else if (tag == SOCKS_CONNECT_REPLY_2)
  443. {
  444. // See socksConnect method for socks reply format
  445. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_CONNECT_REPLY_2: %@", data);
  446. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didConnectToHost:port:)]) {
  447. dispatch_async(self.delegateQueue, ^{
  448. @autoreleasepool {
  449. [self.delegate socket:self didConnectToHost:self.destinationHost port:self.destinationPort];
  450. }
  451. });
  452. }
  453. }
  454. else if (tag == SOCKS_AUTH_USERPASS) {
  455. /*
  456. Server response for username/password authentication:
  457. field 1: version, 1 byte
  458. field 2: status code, 1 byte.
  459. 0x00 = success
  460. any other value = failure, connection must be closed
  461. */
  462. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: SOCKS_AUTH_USERPASS: %@", data);
  463. if (data.length == 2) {
  464. uint8_t *bytes = (uint8_t*)[data bytes];
  465. uint8_t status = bytes[1];
  466. if (status == 0x00) {
  467. [self socksConnect];
  468. } else {
  469. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: Invalid SOCKS username/password auth");
  470. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:104 userInfo:@{NSLocalizedDescriptionKey: @"Invalid SOCKS username/password auth"}];
  471. [self.proxySocket disconnect];
  472. return;
  473. }
  474. } else {
  475. DDLogVerbose(@"GCDAsyncSOCKSProxySocket: Invalid SOCKS username/password response length");
  476. self.lastDisconnectError = [NSError errorWithDomain:@"socks" code:105 userInfo:@{NSLocalizedDescriptionKey: @"Invalid SOCKS username/password response length"}];
  477. [self.proxySocket disconnect];
  478. return;
  479. }
  480. }
  481. else {
  482. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didReadData:withTag:)]) {
  483. dispatch_async(self.delegateQueue, ^{
  484. @autoreleasepool {
  485. [self.delegate socket:self didReadData:data withTag:tag];
  486. }
  487. });
  488. }
  489. }
  490. }
  491. #pragma mark GCDAsyncSocketDelegate methods
  492. - (void) socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
  493. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didWriteDataWithTag:)]) {
  494. dispatch_async(self.delegateQueue, ^{
  495. @autoreleasepool {
  496. [self.delegate socket:self didWriteDataWithTag:tag];
  497. }
  498. });
  499. }
  500. }
  501. - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
  502. DDLogVerbose(@"proxySocket disconnected from proxy %@:%d / destination %@:%d", self.proxyHost, self.proxyPort, self.destinationHost, self.self.destinationPort);
  503. if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidDisconnect:withError:)]) {
  504. dispatch_async(self.delegateQueue, ^{
  505. @autoreleasepool {
  506. [self.delegate socketDidDisconnect:self withError:(err != nil ? err : self.lastDisconnectError)];
  507. }
  508. });
  509. }
  510. }
  511. - (void) socketDidSecure:(GCDAsyncSocket *)sock {
  512. DDLogVerbose(@"didSecure proxy %@:%d / destination %@:%d", self.proxyHost, self.proxyPort, self.destinationHost, self.self.destinationPort);
  513. if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidSecure:)]) {
  514. dispatch_async(self.delegateQueue, ^{
  515. @autoreleasepool {
  516. [self.delegate socketDidSecure:self];
  517. }
  518. });
  519. }
  520. }
  521. - (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock
  522. {
  523. if (self.delegate && [self.delegate respondsToSelector:@selector(socketDidCloseReadStream:)]) {
  524. dispatch_async(self.delegateQueue, ^{
  525. @autoreleasepool {
  526. [self.delegate socketDidCloseReadStream:self];
  527. }
  528. });
  529. }
  530. }
  531. - (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag
  532. {
  533. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didWritePartialDataOfLength:tag:)]) {
  534. dispatch_async(self.delegateQueue, ^{
  535. @autoreleasepool {
  536. [self.delegate socket:self didWritePartialDataOfLength:partialLength tag:tag];
  537. }
  538. });
  539. }
  540. }
  541. - (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL))completionHandler
  542. {
  543. if (self.delegate && [self.delegate respondsToSelector:@selector(socket:didReceiveTrust:completionHandler:)]) {
  544. dispatch_async(self.delegateQueue, ^{
  545. @autoreleasepool {
  546. [self.delegate socket:self didReceiveTrust:trust completionHandler:completionHandler];
  547. }
  548. });
  549. }
  550. }
  551. @end