ServerConnector.m 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2012-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. #include <CommonCrypto/CommonDigest.h>
  21. #import "ServerConnector.h"
  22. #import "NSString+Hex.h"
  23. #import "BoxedMessage.h"
  24. #import "MyIdentityStore.h"
  25. #import "ProtocolDefines.h"
  26. #import "Reachability.h"
  27. #import "MessageQueue.h"
  28. #import "MessageProcessorProxy.h"
  29. #import "Utils.h"
  30. #import "ContactStore.h"
  31. #import "UserSettings.h"
  32. #import "BundleUtil.h"
  33. #import "AppGroup.h"
  34. #import "LicenseStore.h"
  35. #import "PushPayloadDecryptor.h"
  36. #import "ValidationLogger.h"
  37. #import "GCDAsyncSocketFactory.h"
  38. #ifdef DEBUG
  39. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  40. #else
  41. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  42. #endif
  43. #define LOG_KEY_INFO 0
  44. @implementation ServerConnector {
  45. NSData *clientTempKeyPub;
  46. NSData *clientTempKeySec;
  47. time_t clientTempKeyGenTime;
  48. NSData *clientCookie;
  49. NSData *serverCookie;
  50. NSData *serverTempKeyPub;
  51. NSString *serverNamePrefix;
  52. NSString *serverNamePrefixv6;
  53. NSString *serverNameSuffix;
  54. NSArray *serverPorts;
  55. int curServerPortIndex;
  56. NSData *chosenServerKeyPub;
  57. NSData *serverKeyPub;
  58. NSData *serverAltKeyPub;
  59. uint64_t serverNonce;
  60. uint64_t clientNonce;
  61. dispatch_queue_t queue;
  62. dispatch_source_t keepalive_timer;
  63. NSCondition *disconnectCondition;
  64. GCDAsyncSocket *socket;
  65. int reconnectAttempts;
  66. enum ConnectionState connectionState;
  67. BOOL autoReconnect;
  68. CFTimeInterval lastRead;
  69. NSDate *lastErrorDisplay;
  70. CFTimeInterval lastEchoSendTime;
  71. uint64_t lastSentEchoSeq;
  72. uint64_t lastRcvdEchoSeq;
  73. Reachability *internetReachability;
  74. NetworkStatus lastInternetStatus;
  75. NSMutableSet *displayedServerAlerts;
  76. int anotherConnectionCount;
  77. BOOL serverInInitialQueueSend;
  78. BOOL isWaitingForReconnect;
  79. }
  80. @synthesize connectionState;
  81. @synthesize lastRtt;
  82. #pragma pack(push, 1)
  83. #pragma pack(1)
  84. struct pktClientHello {
  85. unsigned char client_tempkey_pub[kNaClCryptoPubKeySize];
  86. unsigned char client_cookie[kCookieLen];
  87. };
  88. struct pktServerHelloBox {
  89. unsigned char server_tempkey_pub[kNaClCryptoPubKeySize];
  90. unsigned char client_cookie[kCookieLen];
  91. };
  92. struct pktServerHello {
  93. unsigned char server_cookie[kCookieLen];
  94. char box[sizeof(struct pktServerHelloBox) + kNaClBoxOverhead];
  95. };
  96. struct pktVouch {
  97. unsigned char client_tempkey_pub[kNaClCryptoPubKeySize];
  98. };
  99. struct pktLogin {
  100. char identity[kIdentityLen];
  101. char client_version[kClientVersionLen];
  102. unsigned char server_cookie[kCookieLen];
  103. unsigned char vouch_nonce[kNaClCryptoNonceSize];
  104. char vouch_box[sizeof(struct pktVouch) + kNaClBoxOverhead];
  105. };
  106. struct pktLoginAck {
  107. char reserved[kLoginAckReservedLen];
  108. };
  109. struct pktPayload {
  110. uint8_t type;
  111. uint8_t reserved[3];
  112. char data[];
  113. };
  114. #pragma pack(pop)
  115. #define TAG_CLIENT_HELLO_SENT 1
  116. #define TAG_SERVER_HELLO_READ 2
  117. #define TAG_LOGIN_SENT 3
  118. #define TAG_LOGIN_ACK_READ 4
  119. #define TAG_PAYLOAD_SENT 5
  120. #define TAG_PAYLOAD_LENGTH_READ 6
  121. #define TAG_PAYLOAD_READ 7
  122. + (ServerConnector*)sharedServerConnector {
  123. static ServerConnector *instance;
  124. @synchronized (self) {
  125. if (!instance)
  126. instance = [[ServerConnector alloc] init];
  127. }
  128. return instance;
  129. }
  130. - (id)init
  131. {
  132. self = [super init];
  133. if (self) {
  134. /* Read server info */
  135. if ([LicenseStore requiresLicenseKey]) {
  136. serverNamePrefix = [BundleUtil objectForInfoDictionaryKey:@"ThreemaWorkServerNamePrefix"];
  137. serverNamePrefixv6 = [BundleUtil objectForInfoDictionaryKey:@"ThreemaWorkServerNamePrefixv6"];
  138. } else {
  139. serverNamePrefix = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerNamePrefix"];
  140. serverNamePrefixv6 = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerNamePrefixv6"];
  141. }
  142. serverNameSuffix = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerNameSuffix"];
  143. serverPorts = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerPorts"];
  144. curServerPortIndex = 0;
  145. serverKeyPub = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerPublicKey"];
  146. serverAltKeyPub = [BundleUtil objectForInfoDictionaryKey:@"ThreemaServerAltPublicKey"];
  147. queue = dispatch_queue_create("ch.threema.SocketQueue", NULL);
  148. disconnectCondition = [[NSCondition alloc] init];
  149. reconnectAttempts = 0;
  150. lastSentEchoSeq = 0;
  151. lastRcvdEchoSeq = 0;
  152. self.connectionState = ConnectionStateDisconnected;
  153. displayedServerAlerts = [NSMutableSet set];
  154. /* register with reachability API */
  155. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatusDidChange:) name:kReachabilityChangedNotification object:nil];
  156. internetReachability = [Reachability reachabilityForInternetConnection];
  157. lastInternetStatus = [internetReachability currentReachabilityStatus];
  158. [internetReachability startNotifier];
  159. isWaitingForReconnect = false;
  160. /* listen for identity changes */
  161. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(identityCreated:) name:kNotificationCreatedIdentity object:nil];
  162. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(identityDestroyed:) name:kNotificationDestroyedIdentity object:nil];
  163. }
  164. return self;
  165. }
  166. - (void)connect {
  167. dispatch_async(queue, ^{
  168. lastErrorDisplay = nil;
  169. [self _connect];
  170. });
  171. }
  172. - (void)connectWait {
  173. dispatch_sync(queue, ^{
  174. lastErrorDisplay = nil;
  175. [self _connect];
  176. });
  177. }
  178. - (void)_connect {
  179. if ([[NSUserDefaults standardUserDefaults] boolForKey:@"FASTLANE_SNAPSHOT"]) {
  180. return;
  181. }
  182. if (![[MyIdentityStore sharedMyIdentityStore] isProvisioned]) {
  183. DDLogInfo(@"Cannot connect - missing identity or key");
  184. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Cannot connect - missing identity or key"]];
  185. return;
  186. }
  187. if (self.connectionState == ConnectionStateDisconnecting) {
  188. // The socketDidDisconnect callback has not been called yet; ensure that we reconnect
  189. // as soon as the previous disconnect has finished.
  190. reconnectAttempts = 1;
  191. autoReconnect = YES;
  192. return;
  193. } else if (self.connectionState != ConnectionStateDisconnected) {
  194. if (self.connectionState == ConnectionStateLoggedIn) {
  195. return;
  196. }
  197. NSString *error = [NSString stringWithFormat:@"Cannot connect - invalid connection state (actual state: %u)", self.connectionState];
  198. DDLogInfo(@"%@", error);
  199. [[ValidationLogger sharedValidationLogger] logString:error];
  200. autoReconnect = YES;
  201. [self reconnectAfterDelay];
  202. return;
  203. }
  204. if ([AppGroup amIActive] == NO) {
  205. DDLogInfo(@"Not active -> don't connect now, retry later");
  206. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Not active -> don't connect now, retry later"]];
  207. // keep delay at constant rate to avoid too long waits when becoming active again
  208. reconnectAttempts = 1;
  209. autoReconnect = YES;
  210. [self reconnectAfterDelay];
  211. return;
  212. }
  213. LicenseStore *licenseStore = [LicenseStore sharedLicenseStore];
  214. if ([licenseStore isValid] == NO) {
  215. [licenseStore performLicenseCheckWithCompletion:^(BOOL success) {
  216. if (success) {
  217. [self connect];
  218. } else {
  219. // don't show license warning for connection errors
  220. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"License check failed: %@", licenseStore.error]];
  221. if ([licenseStore.error.domain hasPrefix:@"NSURL"] == NO) {
  222. // License check failed permanently; need to inform user and ask for new license username/password
  223. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationLicenseMissing object:nil];
  224. } else {
  225. // License check failed due to connection error – try again later
  226. autoReconnect = YES;
  227. [self reconnectAfterDelay];
  228. }
  229. }
  230. }];
  231. return;
  232. }
  233. self.connectionState = ConnectionStateConnecting;
  234. autoReconnect = YES;
  235. self.lastRtt = -1;
  236. lastRead = CACurrentMediaTime();
  237. serverInInitialQueueSend = YES;
  238. /* Generate a new key pair for the server connection. */
  239. time_t uptime = [Utils systemUptime];
  240. DDLogVerbose(@"System uptime is %ld", uptime);
  241. if (clientTempKeyPub == nil || clientTempKeySec == nil || uptime <= 0 || (uptime - clientTempKeyGenTime) > kClientTempKeyMaxAge) {
  242. NSData *publicKey, *secretKey;
  243. [[NaClCrypto sharedCrypto] generateKeyPairPublicKey:&publicKey secretKey:&secretKey];
  244. clientTempKeyPub = publicKey;
  245. clientTempKeySec = secretKey;
  246. clientTempKeyGenTime = uptime;
  247. #if LOG_KEY_INFO
  248. DDLogVerbose(@"Client tempkey_pub = %@, tempkey_sec = %@", clientTempKeyPub, clientTempKeySec);
  249. #endif
  250. }
  251. /* Determine server host name */
  252. NSString *serverHost;
  253. NSTimeInterval timeout = kConnectTimeout;
  254. if ([UserSettings sharedUserSettings].enableIPv6) {
  255. serverHost = [NSString stringWithFormat:@"%@%@%@", serverNamePrefixv6, [MyIdentityStore sharedMyIdentityStore].serverGroup, serverNameSuffix];
  256. } else {
  257. serverHost = [NSString stringWithFormat:@"%@%@%@", serverNamePrefix, [MyIdentityStore sharedMyIdentityStore].serverGroup, serverNameSuffix];
  258. }
  259. NSNumber* serverPort = [serverPorts objectAtIndex:curServerPortIndex];
  260. socket = [GCDAsyncSocketFactory proxyAwareAsyncSocketForHost:serverHost port:serverPort delegate:self delegateQueue:queue];
  261. if ([UserSettings sharedUserSettings].enableIPv6) {
  262. [socket setIPv4PreferredOverIPv6:NO];
  263. } else {
  264. [socket setIPv4PreferredOverIPv6:YES];
  265. }
  266. DDLogInfo(@"Connecting to %@:%@...", serverHost, serverPort);
  267. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Connecting to %@:%@...", serverHost, serverPort]];
  268. NSError *error;
  269. if (![socket connectToHost:serverHost onPort:[serverPort intValue] withTimeout:timeout error:&error]) {
  270. DDLogWarn(@"Connect failed: %@", error);
  271. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Connect failed: %@", error]];
  272. self.connectionState = ConnectionStateDisconnected;
  273. [self reconnectAfterDelay];
  274. return;
  275. }
  276. /* Reset nonces for new connection */
  277. clientNonce = 1;
  278. serverNonce = 1;
  279. }
  280. - (void)_disconnect {
  281. if (connectionState == ConnectionStateDisconnected) {
  282. return;
  283. }
  284. /* disconnect socket and make sure we don't reconnect */
  285. autoReconnect = NO;
  286. self.connectionState = ConnectionStateDisconnecting;
  287. [self _disconnectSocketWithTimeout];
  288. }
  289. - (void)_disconnectSocketWithTimeout {
  290. // Give the socket time for pending writes, but force disconnect if it takes too long for them to complete
  291. [socket disconnectAfterWriting];
  292. GCDAsyncSocket *socketToDisconnect = socket;
  293. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDisconnectTimeout * NSEC_PER_SEC)), queue, ^{
  294. if (socket == socketToDisconnect) {
  295. DDLogInfo(@"Socket still not disconnected - forcing disconnect now");
  296. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Socket still not disconnected - forcing disconnect now"]];
  297. [socket disconnect];
  298. }
  299. });
  300. }
  301. - (void)disconnect {
  302. dispatch_async(queue, ^{
  303. [self _disconnect];
  304. });
  305. }
  306. - (void)disconnectWait {
  307. dispatch_sync(queue, ^{
  308. [self _disconnect];
  309. });
  310. [disconnectCondition lock];
  311. if (connectionState != ConnectionStateDisconnected) {
  312. [disconnectCondition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:kDisconnectTimeout]];
  313. }
  314. // Note: it's not guaranteed that the state is actually disconnected at this point, but it's good enough for our purposes
  315. [disconnectCondition unlock];
  316. }
  317. - (void)reconnect {
  318. dispatch_async(queue, ^{
  319. if (connectionState == ConnectionStateDisconnected) {
  320. [self _connect];
  321. } else if (connectionState == ConnectionStateConnecting) {
  322. DDLogVerbose(@"Connection already in progress, not reconnecting");
  323. } else {
  324. autoReconnect = YES;
  325. self.connectionState = ConnectionStateDisconnecting;
  326. [self _disconnectSocketWithTimeout];
  327. }
  328. });
  329. }
  330. - (void)setConnectionState:(enum ConnectionState)newConnectionState {
  331. [disconnectCondition lock];
  332. connectionState = newConnectionState;
  333. if (connectionState == ConnectionStateDisconnected) {
  334. [disconnectCondition broadcast];
  335. }
  336. [disconnectCondition unlock];
  337. }
  338. - (void)processPayload:(struct pktPayload*)pl datalen:(int)datalen {
  339. switch (pl->type) {
  340. case PLTYPE_ECHO_REPLY: {
  341. self.lastRtt = CACurrentMediaTime() - lastEchoSendTime;
  342. if (datalen == sizeof(lastRcvdEchoSeq)) {
  343. memcpy(&lastRcvdEchoSeq, pl->data, sizeof(lastRcvdEchoSeq));
  344. } else {
  345. DDLogError(@"Bad echo reply datalen %d", datalen);
  346. [socket disconnect];
  347. break;
  348. }
  349. DDLogInfo(@"Received echo reply (seq %llu, RTT %.1f ms)", lastRcvdEchoSeq, self.lastRtt * 1000);
  350. break;
  351. }
  352. case PLTYPE_ERROR: {
  353. if (datalen < sizeof(struct plError)) {
  354. DDLogError(@"Bad error payload datalen %d", datalen);
  355. [socket disconnect];
  356. break;
  357. }
  358. struct plError *plerr = (struct plError*)pl->data;
  359. NSData *errorMessageData = [NSData dataWithBytes:plerr->err_message length:datalen - sizeof(struct plError)];
  360. NSString *errorMessage = [[NSString alloc] initWithData:errorMessageData encoding:NSUTF8StringEncoding];
  361. DDLogError(@"Received error message from server: %@", errorMessage);
  362. if ([errorMessage rangeOfString:@"Another connection"].location != NSNotFound) {
  363. // extension took over connection
  364. if ([AppGroup amIActive] == NO) {
  365. break;
  366. }
  367. // ignore first few occurrences of "Another connection" messages to gracefully handle network switches
  368. if (anotherConnectionCount < 5) {
  369. anotherConnectionCount++;
  370. break;
  371. }
  372. }
  373. if (!plerr->reconnect_allowed) {
  374. autoReconnect = NO;
  375. }
  376. if (lastErrorDisplay == nil || ((-[lastErrorDisplay timeIntervalSinceNow]) > kErrorDisplayInterval)) {
  377. lastErrorDisplay = [NSDate date];
  378. NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
  379. errorMessage, kKeyMessage,
  380. nil];
  381. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationErrorConnectionFailed object:nil userInfo:info];
  382. }
  383. break;
  384. }
  385. case PLTYPE_ALERT: {
  386. NSData *alertData = [NSData dataWithBytes:pl->data length:datalen];
  387. NSString *alertText = [[NSString alloc] initWithData:alertData encoding:NSUTF8StringEncoding];
  388. [self displayServerAlert:alertText];
  389. break;
  390. }
  391. case PLTYPE_OUTGOING_MESSAGE_ACK: {
  392. if (datalen != sizeof(struct plMessageAck)) {
  393. DDLogError(@"Bad ACK payload datalen %d", datalen);
  394. [socket disconnect];
  395. break;
  396. }
  397. struct plMessageAck *ack = (struct plMessageAck*)pl->data;
  398. /* ignore from identity, as it must be ours */
  399. NSData *messageId = [NSData dataWithBytes:ack->message_id length:kMessageIdLen];
  400. [[MessageQueue sharedMessageQueue] processAck:messageId];
  401. break;
  402. }
  403. case PLTYPE_INCOMING_MESSAGE: {
  404. if (datalen <= sizeof(struct plMessage)) {
  405. DDLogError(@"Bad message payload datalen %d", datalen);
  406. [socket disconnect];
  407. break;
  408. }
  409. struct plMessage *plmsg = (struct plMessage*)pl->data;
  410. BoxedMessage *boxmsg = [[BoxedMessage alloc] init];
  411. boxmsg.fromIdentity = [[NSString alloc] initWithData:[NSData dataWithBytes:plmsg->from_identity length:kIdentityLen] encoding:NSASCIIStringEncoding];
  412. boxmsg.toIdentity = [[NSString alloc] initWithData:[NSData dataWithBytes:plmsg->to_identity length:kIdentityLen] encoding:NSASCIIStringEncoding];
  413. boxmsg.messageId = [NSData dataWithBytes:plmsg->message_id length:kMessageIdLen];
  414. boxmsg.date = [NSDate dateWithTimeIntervalSince1970:plmsg->date];
  415. boxmsg.flags = plmsg->flags;
  416. char pushFromNameT[kPushFromNameLen+1];
  417. memcpy(pushFromNameT, plmsg->push_from_name, kPushFromNameLen);
  418. pushFromNameT[kPushFromNameLen] = 0;
  419. boxmsg.pushFromName = [NSString stringWithCString:pushFromNameT encoding:NSUTF8StringEncoding];
  420. boxmsg.nonce = [NSData dataWithBytes:plmsg->nonce length:kNonceLen];
  421. boxmsg.box = [NSData dataWithBytes:plmsg->box length:(datalen - sizeof(struct plMessage))];
  422. [MessageProcessorProxy processIncomingMessage:boxmsg receivedAfterInitialQueueSend:!serverInInitialQueueSend onCompletion:^{
  423. [self completedProcessingMessage:boxmsg];
  424. } onError:^(NSError *err) {
  425. [self failedProcessingMessage:boxmsg error:err];
  426. }];
  427. break;
  428. }
  429. case PLTYPE_QUEUE_SEND_COMPLETE:
  430. DDLogInfo(@"Queue send complete");
  431. serverInInitialQueueSend = NO;
  432. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationQueueSendComplete object:nil userInfo:nil];
  433. break;
  434. default:
  435. DDLogWarn(@"Unsupported payload type %d", pl->type);
  436. break;
  437. }
  438. }
  439. - (void)completedProcessingMessage:(BoxedMessage *)boxmsg {
  440. if (!(boxmsg.flags & MESSAGE_FLAG_NOACK)) {
  441. /* send ACK to server */
  442. [self ackMessage:boxmsg.messageId fromIdentity:boxmsg.fromIdentity];
  443. }
  444. }
  445. - (void)completedProcessingAbstractMessage:(AbstractGroupMessage *)abstractGroupMsg {
  446. uint8_t flags = abstractGroupMsg.flags.unsignedCharValue;
  447. if (!(flags & MESSAGE_FLAG_NOACK)) {
  448. /* send ACK to server */
  449. [self ackMessage:abstractGroupMsg.messageId fromIdentity:abstractGroupMsg.fromIdentity];
  450. }
  451. }
  452. - (void)failedProcessingMessage:(BoxedMessage *)boxmsg error:(NSError *)err {
  453. if (err.code == kBlockUnknownContactErrorCode) {
  454. DDLogVerbose(@"Message processing error due to block contacts - acking anyway");
  455. [self ackMessage:boxmsg.messageId fromIdentity:boxmsg.fromIdentity];
  456. } else if (err.code == kBadMessageErrorCode) {
  457. DDLogVerbose(@"Message processing error due to bad message format or decryption failure - acking anyway");
  458. [self ackMessage:boxmsg.messageId fromIdentity:boxmsg.fromIdentity];
  459. } else if (err.code == kMessageProcessingErrorCode) {
  460. DDLogError(@"Message processing error due to being unable to handle message: %@", err);
  461. } else {
  462. DDLogInfo(@"Could not process incoming message: %@", err);
  463. }
  464. }
  465. - (void)reconnectAfterDelay {
  466. if (!autoReconnect) {
  467. return;
  468. }
  469. /* calculate delay using bound exponential backoff */
  470. float reconnectDelay = powf(kReconnectBaseInterval, MIN(reconnectAttempts - 1, 10));
  471. if (reconnectDelay > kReconnectMaxInterval) {
  472. reconnectDelay = kReconnectMaxInterval;
  473. }
  474. if (!isWaitingForReconnect) {
  475. isWaitingForReconnect = true;
  476. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Waiting %f seconds before reconnecting", reconnectDelay]];
  477. reconnectAttempts++;
  478. DDLogInfo(@"Waiting %f seconds before reconnecting", reconnectDelay);
  479. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, reconnectDelay * NSEC_PER_SEC);
  480. dispatch_after(popTime, queue, ^(void){
  481. isWaitingForReconnect = false;
  482. [self _connect];
  483. });
  484. }
  485. }
  486. - (void)sendPayloadWithType:(uint8_t)type data:(NSData*)data {
  487. if (connectionState != ConnectionStateLoggedIn) {
  488. DDLogVerbose(@"Cannot send payload - not logged in");
  489. return;
  490. }
  491. dispatch_async(queue, ^{
  492. [self sendPayloadAsyncWithType:type data:data];
  493. });
  494. }
  495. - (void)sendPayloadAsyncWithType:(uint8_t)type data:(NSData*)data {
  496. /* Make encrypted box */
  497. unsigned long pllen = sizeof(struct pktPayload) + data.length;
  498. struct pktPayload *pl = malloc(pllen);
  499. if (!pl) {
  500. return;
  501. }
  502. bzero(pl, pllen);
  503. pl->type = type;
  504. memcpy(pl->data, data.bytes, data.length);
  505. NSData *plData = [NSData dataWithBytesNoCopy:pl length:pllen];
  506. NSData *nextClientNonce = [self nextClientNonce];
  507. NSData *plBox = [[NaClCrypto sharedCrypto] encryptData:plData withPublicKey:serverTempKeyPub signKey:clientTempKeySec nonce:nextClientNonce];
  508. if (plBox == nil) {
  509. DDLogError(@"Payload encryption failed!");
  510. return;
  511. }
  512. /* prepend length - make one NSData object to pass to socket to ensure it is sent
  513. in a single TCP segment */
  514. uint16_t pktlen = plBox.length;
  515. if (pktlen > kMaxPktLen) {
  516. DDLogError(@"Packet is too big (%d) - cannot send", pktlen);
  517. return;
  518. }
  519. NSMutableData *sendData = [NSMutableData dataWithCapacity:plBox.length + sizeof(uint16_t)];
  520. [sendData appendBytes:&pktlen length:sizeof(uint16_t)];
  521. [sendData appendData:plBox];
  522. [socket writeData:sendData withTimeout:kWriteTimeout tag:TAG_PAYLOAD_SENT];
  523. return;
  524. }
  525. - (void)sendMessage:(BoxedMessage*)message {
  526. unsigned long msglen = sizeof(struct plMessage) + message.box.length;
  527. struct plMessage *plmsg = malloc(msglen);
  528. if (!plmsg) {
  529. return;
  530. }
  531. DDLogInfo(@"Sending message from %@ to %@ (ID %@), box length %lu", message.fromIdentity,
  532. message.toIdentity, message.messageId, (unsigned long)message.box.length);
  533. memcpy(plmsg->from_identity, [message.fromIdentity dataUsingEncoding:NSASCIIStringEncoding].bytes, kIdentityLen);
  534. memcpy(plmsg->to_identity, [message.toIdentity dataUsingEncoding:NSASCIIStringEncoding].bytes, kIdentityLen);
  535. memcpy(plmsg->message_id, message.messageId.bytes, kMessageIdLen);
  536. plmsg->date = [message.date timeIntervalSince1970];
  537. plmsg->flags = message.flags;
  538. plmsg->reserved[0] = 0; plmsg->reserved[1] = 0; plmsg->reserved[2] = 0;
  539. bzero(plmsg->push_from_name, kPushFromNameLen);
  540. if (message.pushFromName != nil) {
  541. NSData *encodedPushFromName = [Utils truncatedUTF8String:message.pushFromName maxLength:kPushFromNameLen];
  542. strncpy(plmsg->push_from_name, encodedPushFromName.bytes, encodedPushFromName.length);
  543. }
  544. memcpy(plmsg->nonce, message.nonce.bytes, kNaClCryptoNonceSize);
  545. memcpy(plmsg->box, message.box.bytes, message.box.length);
  546. [self sendPayloadWithType:PLTYPE_OUTGOING_MESSAGE data:[NSData dataWithBytesNoCopy:plmsg length:msglen]];
  547. }
  548. - (void)ackMessage:(NSData*)messageId fromIdentity:(NSString*)fromIdentity {
  549. int msglen = sizeof(struct plMessageAck);
  550. struct plMessageAck *plmsgack = malloc(msglen);
  551. if (!plmsgack)
  552. return;
  553. DDLogInfo(@"Sending ack for message ID %@ from %@", messageId, fromIdentity);
  554. memcpy(plmsgack->from_identity, [fromIdentity dataUsingEncoding:NSASCIIStringEncoding].bytes, kIdentityLen);
  555. memcpy(plmsgack->message_id, messageId.bytes, kMessageIdLen);
  556. [self sendPayloadWithType:PLTYPE_INCOMING_MESSAGE_ACK data:[NSData dataWithBytesNoCopy:plmsgack length:msglen]];
  557. }
  558. - (void)ping {
  559. dispatch_async(queue, ^{
  560. [self sendEchoRequest];
  561. });
  562. }
  563. - (void)sendEchoRequest {
  564. if (connectionState != ConnectionStateLoggedIn)
  565. return;
  566. lastSentEchoSeq++;
  567. DDLogInfo(@"Sending echo request (seq %llu)", lastSentEchoSeq);
  568. lastEchoSendTime = CACurrentMediaTime();
  569. [self sendPayloadAsyncWithType:PLTYPE_ECHO_REQUEST data:[NSData dataWithBytes:&lastSentEchoSeq length:sizeof(lastSentEchoSeq)]];
  570. GCDAsyncSocket *curSocket = socket;
  571. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kReadTimeout * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
  572. if (curSocket == socket && lastRcvdEchoSeq < lastSentEchoSeq) {
  573. DDLogInfo(@"No reply to echo payload; disconnecting");
  574. [socket disconnect];
  575. }
  576. });
  577. }
  578. - (void)cleanPushToken {
  579. if ([[AppGroup userDefaults] objectForKey:kPushNotificationDeviceToken] != nil) {
  580. [[AppGroup userDefaults] setObject:nil forKey:kPushNotificationDeviceToken];
  581. [[AppGroup userDefaults] synchronize];
  582. }
  583. [self sendPushToken];
  584. [self sendPushAllowedIdentities];
  585. [self sendPushSound];
  586. }
  587. - (void)setVoIPPushToken:(NSData *)voIPPushToken {
  588. [[AppGroup userDefaults] setObject:voIPPushToken forKey:kVoIPPushNotificationDeviceToken];
  589. [[AppGroup userDefaults] synchronize];
  590. [self sendVoIPPushToken];
  591. }
  592. - (void)sendPushToken {
  593. if ([self shouldRegisterPush] == NO) {
  594. return;
  595. }
  596. DDLogInfo(@"Sending push notification token");
  597. uint8_t pushTokenType = PUSHTOKEN_TYPE_NONE;
  598. NSMutableData *payloadData = [NSMutableData dataWithBytes:&pushTokenType length:1];
  599. [self sendPayloadWithType:PLTYPE_PUSH_NOTIFICATION_TOKEN data:payloadData];
  600. }
  601. - (void)sendVoIPPushToken {
  602. if ([self shouldRegisterVoIP] == NO) {
  603. return;
  604. }
  605. NSData *voIPPushToken = [[AppGroup userDefaults] objectForKey:kVoIPPushNotificationDeviceToken];
  606. DDLogInfo(@"Sending VoIP push notification token");
  607. uint8_t voIPPushTokenType;
  608. #ifdef DEBUG
  609. voIPPushTokenType = PUSHTOKEN_TYPE_APPLE_SANDBOX;
  610. #else
  611. voIPPushTokenType = PUSHTOKEN_TYPE_APPLE_PROD;
  612. #endif
  613. NSMutableData *payloadData = [NSMutableData dataWithBytes:&voIPPushTokenType length:1];
  614. [payloadData appendData:voIPPushToken];
  615. [payloadData appendData:[@"|" dataUsingEncoding:NSUTF8StringEncoding]];
  616. [payloadData appendData:[[[NSBundle mainBundle] bundleIdentifier] dataUsingEncoding:NSASCIIStringEncoding]];
  617. [payloadData appendData:[@"|" dataUsingEncoding:NSUTF8StringEncoding]];
  618. [payloadData appendData:[PushPayloadDecryptor pushEncryptionKey]];
  619. [self sendPayloadWithType:PLTYPE_VOIP_PUSH_NOTIFICATION_TOKEN data:payloadData];
  620. }
  621. - (void)sendPushAllowedIdentities {
  622. if ([self shouldRegisterPush] == NO) {
  623. return;
  624. }
  625. // Disable filter by allowing all IDs; we filter pushes in our own logic now
  626. DDLogInfo(@"Sending push allowed identities");
  627. dispatch_async(dispatch_get_main_queue(), ^{
  628. NSData *iddata = [NSData dataWithBytes:"\0" length:1];
  629. DDLogVerbose(@"Sending allowed identities: %@", iddata);
  630. [self sendPayloadWithType:PLTYPE_PUSH_ALLOWED_IDENTITIES data:iddata];
  631. });
  632. }
  633. - (BOOL)shouldRegisterPush {
  634. if (connectionState != ConnectionStateLoggedIn) {
  635. return NO;
  636. }
  637. if ([AppGroup getCurrentType] != AppGroupTypeApp) {
  638. // only register within main app for pushes
  639. return NO;
  640. }
  641. return YES;
  642. }
  643. - (BOOL)shouldRegisterVoIP {
  644. if (connectionState != ConnectionStateLoggedIn) {
  645. return NO;
  646. }
  647. if ([[AppGroup userDefaults] objectForKey:kVoIPPushNotificationDeviceToken] == nil) {
  648. return NO;
  649. }
  650. if ([AppGroup getCurrentType] != AppGroupTypeApp) {
  651. // only register within main app for pushes
  652. return NO;
  653. }
  654. return YES;
  655. }
  656. - (void)sendPushSound{
  657. if ([self shouldRegisterPush] == NO) {
  658. return;
  659. }
  660. NSString *pushSound = @"";
  661. DDLogInfo(@"Sending push sound: %@", pushSound);
  662. [self sendPayloadWithType:PLTYPE_PUSH_SOUND data:[pushSound dataUsingEncoding:NSASCIIStringEncoding]];
  663. }
  664. - (void)sendPushGroupSound {
  665. if ([self shouldRegisterPush] == NO) {
  666. return;
  667. }
  668. NSString *pushGroupSound = @"";
  669. DDLogInfo(@"Sending push group sound: %@", pushGroupSound);
  670. [self sendPayloadWithType:PLTYPE_PUSH_GROUP_SOUND data:[pushGroupSound dataUsingEncoding:NSASCIIStringEncoding]];
  671. }
  672. - (NSData*)nextClientNonce {
  673. char nonce[kNaClCryptoNonceSize];
  674. memcpy(nonce, clientCookie.bytes, kCookieLen);
  675. memcpy(&nonce[kCookieLen], &clientNonce, sizeof(clientNonce));
  676. clientNonce++;
  677. return [NSData dataWithBytes:nonce length:kNaClCryptoNonceSize];
  678. }
  679. - (NSData*)nextServerNonce {
  680. char nonce[kNaClCryptoNonceSize];
  681. memcpy(nonce, serverCookie.bytes, kCookieLen);
  682. memcpy(&nonce[kCookieLen], &serverNonce, sizeof(serverNonce));
  683. serverNonce++;
  684. return [NSData dataWithBytes:nonce length:kNaClCryptoNonceSize];
  685. }
  686. - (NSString*)nameForConnectionState:(enum ConnectionState)_connectionState {
  687. switch (_connectionState) {
  688. case ConnectionStateDisconnected:
  689. return @"disconnected";
  690. case ConnectionStateConnecting:
  691. return @"connecting";
  692. case ConnectionStateConnected:
  693. return @"connected";
  694. case ConnectionStateLoggedIn:
  695. return @"loggedin";
  696. case ConnectionStateDisconnecting:
  697. return @"disconnecting";
  698. }
  699. return nil;
  700. }
  701. - (BOOL)isIPv6Connection {
  702. return [socket isIPv6];
  703. }
  704. - (BOOL)isProxyConnection {
  705. return (socket != nil && ![socket isMemberOfClass:[GCDAsyncSocket class]]);
  706. }
  707. - (void)displayServerAlert:(NSString*)alertText {
  708. if ([displayedServerAlerts containsObject:alertText])
  709. return;
  710. /* not shown before */
  711. NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
  712. alertText, kKeyMessage,
  713. nil];
  714. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationServerMessage object:nil userInfo:info];
  715. [displayedServerAlerts addObject:alertText];
  716. }
  717. - (void)networkStatusDidChange:(NSNotification *)notice
  718. {
  719. NetworkStatus internetStatus = [internetReachability currentReachabilityStatus];
  720. switch (internetStatus) {
  721. case NotReachable:
  722. DDLogInfo(@"Internet is not reachable");
  723. [[ValidationLogger sharedValidationLogger] logString:@"Internet is not reachable"];
  724. break;
  725. case ReachableViaWiFi:
  726. DDLogInfo(@"Internet is reachable via WiFi");
  727. [[ValidationLogger sharedValidationLogger] logString:@"Internet is reachable via WiFi"];
  728. break;
  729. case ReachableViaWWAN:
  730. DDLogInfo(@"Internet is reachable via WWAN");
  731. [[ValidationLogger sharedValidationLogger] logString:@"Internet is reachable via WWAN"];
  732. break;
  733. }
  734. if (internetStatus != lastInternetStatus) {
  735. DDLogInfo(@"Internet status changed - forcing reconnect");
  736. [[ValidationLogger sharedValidationLogger] logString:@"Internet status changed - forcing reconnect"];
  737. curServerPortIndex = 0;
  738. [self reconnect];
  739. lastInternetStatus = internetStatus;
  740. }
  741. }
  742. - (void)setServerPorts:(NSArray *)ports {
  743. serverPorts = ports;
  744. }
  745. - (void)sendPushOverrideTimeout {
  746. DDLogInfo(@"Sending set push override timeout");
  747. NSUserDefaults *defaults = [AppGroup userDefaults];
  748. NSDate *lastSendDate = [defaults objectForKey:kLastPushOverrideSendDate];
  749. if (lastSendDate == nil) {
  750. [self setPushOverrideTimeout];
  751. } else {
  752. NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitMinute fromDate:[NSDate date] toDate:lastSendDate options:0];
  753. NSInteger minutes = [components minute];
  754. if (minutes > 60 || lastSendDate == nil) {
  755. [self setPushOverrideTimeout];
  756. }
  757. }
  758. }
  759. - (void)setPushOverrideTimeout {
  760. NSUserDefaults *defaults = [AppGroup userDefaults];
  761. NSTimeInterval secondsInEightHours = 8 * 60 * 60;
  762. NSDate *pushOverrideEndDate = [[NSDate date] dateByAddingTimeInterval:secondsInEightHours];
  763. uint64_t timestamp = [pushOverrideEndDate timeIntervalSince1970];
  764. NSData *payloadData = [NSData dataWithBytes:&timestamp length:sizeof(timestamp)];
  765. [self sendPayloadWithType:PLTYPE_PUSH_OVERRIDE_TIMEOUT data:payloadData];
  766. [defaults setObject:[NSDate date] forKey:kLastPushOverrideSendDate];
  767. [defaults synchronize];
  768. }
  769. - (void)resetPushOverrideTimeout {
  770. DDLogInfo(@"Reset push override timeout");
  771. NSUserDefaults *defaults = [AppGroup userDefaults];
  772. uint64_t timestamp = 0;
  773. NSData *data = [NSData dataWithBytes:&timestamp length:sizeof(timestamp)];
  774. [self sendPayloadWithType:PLTYPE_PUSH_OVERRIDE_TIMEOUT data:data];
  775. [defaults setObject:nil forKey:kLastPushOverrideSendDate];
  776. [defaults synchronize];
  777. }
  778. #pragma mark - GCDAsyncSocketDelegate
  779. - (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port {
  780. if (sender != socket) {
  781. DDLogWarn(@"didConnectToHost from old socket");
  782. return;
  783. }
  784. DDLogInfo(@"Connected to %@:%d", host, port);
  785. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Connected to %@:%d", host, port]];
  786. self.connectionState = ConnectionStateConnected;
  787. /* Send client hello packet with temporary public key and client cookie */
  788. clientCookie = [[NaClCrypto sharedCrypto] randomBytes:kCookieLen];
  789. DDLogVerbose(@"Client cookie = %@", clientCookie);
  790. /* Make sure to pass everything in one writeData call, or we will get two separate TCP segments */
  791. struct pktClientHello clientHello;
  792. memcpy(clientHello.client_tempkey_pub, clientTempKeyPub.bytes, sizeof(clientHello.client_tempkey_pub));
  793. memcpy(clientHello.client_cookie, clientCookie.bytes, sizeof(clientHello.client_cookie));
  794. [socket writeData:[NSData dataWithBytes:&clientHello length:sizeof(clientHello)] withTimeout:kWriteTimeout tag:TAG_CLIENT_HELLO_SENT];
  795. /* Prepare to receive server hello packet */
  796. [socket readDataToLength:sizeof(struct pktServerHello) withTimeout:kReadTimeout tag:TAG_SERVER_HELLO_READ];
  797. }
  798. - (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag {
  799. //DDLogVerbose(@"Read data (%d bytes) with tag: %ld", data.length, tag);
  800. if (sender != socket) {
  801. DDLogWarn(@"didReadData from old socket");
  802. return;
  803. }
  804. switch (tag) {
  805. case TAG_SERVER_HELLO_READ: {
  806. DDLogVerbose(@"Got server hello!");
  807. const struct pktServerHello* serverHello = data.bytes;
  808. serverCookie = [NSData dataWithBytes:serverHello->server_cookie length:sizeof(serverHello->server_cookie)];
  809. DDLogVerbose(@"Server cookie = %@", serverCookie);
  810. /* decrypt server hello box */
  811. chosenServerKeyPub = serverKeyPub;
  812. NSData *serverHelloBox = [NSData dataWithBytes:serverHello->box length:sizeof(serverHello->box)];
  813. NSData *nonce = [self nextServerNonce];
  814. NSData *serverHelloBoxOpen = [[NaClCrypto sharedCrypto] decryptData:serverHelloBox withSecretKey:clientTempKeySec signKey:chosenServerKeyPub nonce:nonce];
  815. if (serverHelloBoxOpen == nil) {
  816. /* try alternate key */
  817. chosenServerKeyPub = serverAltKeyPub;
  818. serverHelloBoxOpen = [[NaClCrypto sharedCrypto] decryptData:serverHelloBox withSecretKey:clientTempKeySec signKey:chosenServerKeyPub nonce:nonce];
  819. if (serverHelloBoxOpen == nil) {
  820. DDLogError(@"Decryption of server hello box failed");
  821. [socket disconnect];
  822. return;
  823. } else {
  824. DDLogWarn(@"Using alternate server key!");
  825. }
  826. }
  827. const struct pktServerHelloBox *serverHelloBoxU = (struct pktServerHelloBox*)serverHelloBoxOpen.bytes;
  828. /* verify client cookie */
  829. NSData *clientCookieFromServer = [NSData dataWithBytes:serverHelloBoxU->client_cookie length:sizeof(serverHelloBoxU->client_cookie)];
  830. if (![clientCookieFromServer isEqualToData:clientCookie]) {
  831. DDLogError(@"Client cookie mismatch (mine: %@, server: %@)", clientCookie, clientCookieFromServer);
  832. [socket disconnect];
  833. return;
  834. }
  835. /* copy temporary server key */
  836. serverTempKeyPub = [NSData dataWithBytes:serverHelloBoxU->server_tempkey_pub length:sizeof(serverHelloBoxU->server_tempkey_pub)];
  837. DDLogInfo(@"Server hello successful, tempkey_pub = %@", serverTempKeyPub);
  838. /* now prepare login packet */
  839. NSData *vouchNonce = [[NaClCrypto sharedCrypto] randomBytes:kNaClCryptoNonceSize];
  840. struct pktLogin login;
  841. memcpy(login.identity, [[MyIdentityStore sharedMyIdentityStore].identity dataUsingEncoding:NSASCIIStringEncoding].bytes, kIdentityLen);
  842. bzero(login.client_version, kClientVersionLen);
  843. NSData *clientVersion = [[Utils getClientVersion] dataUsingEncoding:NSASCIIStringEncoding];
  844. memcpy(login.client_version, clientVersion.bytes, MIN(clientVersion.length, kClientVersionLen));
  845. memcpy(login.server_cookie, serverCookie.bytes, kCookieLen);
  846. memcpy(login.vouch_nonce, vouchNonce.bytes, kNaClCryptoNonceSize);
  847. /* vouch subpacket */
  848. struct pktVouch vouch;
  849. memcpy(vouch.client_tempkey_pub, clientTempKeyPub.bytes, kNaClCryptoPubKeySize);
  850. NSData *vouchBox = [[MyIdentityStore sharedMyIdentityStore] encryptData:[NSData dataWithBytes:&vouch length:sizeof(vouch)] withNonce:vouchNonce publicKey:chosenServerKeyPub];
  851. memcpy(login.vouch_box, vouchBox.bytes, sizeof(login.vouch_box));
  852. /* encrypt login packet */
  853. NSData *loginBox = [[NaClCrypto sharedCrypto] encryptData:[NSData dataWithBytes:&login length:sizeof(login)] withPublicKey:serverTempKeyPub signKey:clientTempKeySec nonce:[self nextClientNonce]];
  854. /* send it! */
  855. [socket writeData:loginBox withTimeout:kWriteTimeout tag:TAG_LOGIN_SENT];
  856. /* Prepare to receive login ack packet */
  857. [socket readDataToLength:sizeof(struct pktLoginAck) + kNaClBoxOverhead withTimeout:kReadTimeout tag:TAG_LOGIN_ACK_READ];
  858. break;
  859. }
  860. case TAG_LOGIN_ACK_READ: {
  861. DDLogInfo(@"Login ack received");
  862. lastRead = CACurrentMediaTime();
  863. /* decrypt server hello box */
  864. NSData *loginAckBox = data;
  865. loginAckBox = [[NaClCrypto sharedCrypto] decryptData:loginAckBox withSecretKey:clientTempKeySec signKey:serverTempKeyPub nonce:[self nextServerNonce]];
  866. if (loginAckBox == nil) {
  867. DDLogError(@"Decryption of login ack failed");
  868. [socket disconnect];
  869. return;
  870. }
  871. /* Don't care about the contents of the login ACK for now; it only needs to decrypt correctly */
  872. reconnectAttempts = 0;
  873. self.connectionState = ConnectionStateLoggedIn;
  874. /* Clean and send nil push token info */
  875. [self cleanPushToken];
  876. /* Send voIP push token info */
  877. [self sendVoIPPushToken];
  878. /* Schedule task for keepalive */
  879. keepalive_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
  880. dispatch_source_set_event_handler(keepalive_timer, ^{
  881. [self sendEchoRequest];
  882. });
  883. dispatch_source_set_timer(keepalive_timer, dispatch_time(DISPATCH_TIME_NOW, kKeepAliveInterval * NSEC_PER_SEC),
  884. kKeepAliveInterval * NSEC_PER_SEC, NSEC_PER_SEC);
  885. dispatch_resume(keepalive_timer);
  886. /* Receive next payload header */
  887. [socket readDataToLength:sizeof(uint16_t) withTimeout:-1 tag:TAG_PAYLOAD_LENGTH_READ];
  888. break;
  889. }
  890. case TAG_PAYLOAD_LENGTH_READ: {
  891. uint16_t msglen = *((uint16_t*)data.bytes);
  892. [socket readDataToLength:msglen withTimeout:-1 tag:TAG_PAYLOAD_READ];
  893. break;
  894. }
  895. case TAG_PAYLOAD_READ: {
  896. DDLogVerbose(@"Payload (%lu bytes) received", (unsigned long)data.length);
  897. lastRead = CACurrentMediaTime();
  898. dispatch_source_set_timer(keepalive_timer, dispatch_time(DISPATCH_TIME_NOW, kKeepAliveInterval * NSEC_PER_SEC),
  899. kKeepAliveInterval * NSEC_PER_SEC, NSEC_PER_SEC);
  900. /* Decrypt payload */
  901. NSData *plData = [[NaClCrypto sharedCrypto] decryptData:data withSecretKey:clientTempKeySec signKey:serverTempKeyPub nonce:[self nextServerNonce]];
  902. if (plData == nil) {
  903. DDLogError(@"Payload decryption failed");
  904. [socket disconnect];
  905. return;
  906. }
  907. struct pktPayload *pl = (struct pktPayload*)plData.bytes;
  908. int datalen = (int)plData.length - (int)sizeof(struct pktPayload);
  909. DDLogInfo(@"Decrypted payload (type %02x, data %@)", pl->type, [NSData dataWithBytes:pl->data length:datalen]);
  910. [self processPayload:pl datalen:datalen];
  911. /* Receive next payload header */
  912. [socket readDataToLength:sizeof(uint16_t) withTimeout:-1 tag:TAG_PAYLOAD_LENGTH_READ];
  913. break;
  914. }
  915. }
  916. }
  917. - (void)socketDidDisconnect:(GCDAsyncSocket *)sender withError:(NSError *)error {
  918. [[ValidationLogger sharedValidationLogger] logString:@"socketDidDisconnect called"];
  919. if (sender != socket) {
  920. DDLogWarn(@"socketDidDisconnect from old socket");
  921. [[ValidationLogger sharedValidationLogger] logString:@"socketDidDisconnect from old socket"];
  922. return;
  923. }
  924. if (error != nil) {
  925. DDLogWarn(@"Socket disconnected, error = %@", error);
  926. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Socket disconnected, error = %@", error]];
  927. /* try next port */
  928. curServerPortIndex++;
  929. if (curServerPortIndex >= serverPorts.count)
  930. curServerPortIndex = 0;
  931. }
  932. self.connectionState = ConnectionStateDisconnected;
  933. if (keepalive_timer != nil) {
  934. dispatch_source_cancel(keepalive_timer);
  935. keepalive_timer = nil;
  936. }
  937. socket = nil;
  938. [self reconnectAfterDelay];
  939. }
  940. - (NSTimeInterval)socket:(GCDAsyncSocket *)sender shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length {
  941. if (sender != socket) {
  942. DDLogWarn(@"shouldTimeoutReadWithTag from old socket");
  943. return 0;
  944. }
  945. DDLogInfo(@"Read timeout, tag = %ld", tag);
  946. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Read timeout"]];
  947. [socket disconnect];
  948. return 0;
  949. }
  950. - (NSTimeInterval)socket:(GCDAsyncSocket *)sender shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length {
  951. if (sender != socket) {
  952. DDLogWarn(@"shouldTimeoutWriteWithTag from old socket");
  953. return 0;
  954. }
  955. DDLogInfo(@"Write timeout, tag = %ld", tag);
  956. [[ValidationLogger sharedValidationLogger] logString:[NSString stringWithFormat:@"Write timeout"]];
  957. [socket disconnect];
  958. return 0;
  959. }
  960. #pragma mark - Notifications
  961. - (void)identityCreated:(NSNotification*)notification {
  962. /* when the identity is created, we should connect */
  963. [self connect];
  964. }
  965. - (void)identityDestroyed:(NSNotification*)notification {
  966. /* when the identity is destroyed, we must disconnect */
  967. if (connectionState != ConnectionStateDisconnected) {
  968. DDLogInfo(@"Disconnecting because identity destroyed");
  969. /* Clear push token on server now to reduce occurrence of push messages being
  970. delivered to devices that don't use that particular identity anymore */
  971. DDLogInfo(@"Clearing push notification token");
  972. uint8_t pushTokenType;
  973. #ifdef DEBUG
  974. pushTokenType = PUSHTOKEN_TYPE_APPLE_SANDBOX_MC;
  975. #else
  976. pushTokenType = PUSHTOKEN_TYPE_APPLE_PROD_MC;
  977. #endif
  978. NSMutableData *payloadData = [NSMutableData dataWithBytes:&pushTokenType length:1];
  979. NSData *pushToken = [[NaClCrypto sharedCrypto] zeroBytes:32];
  980. [payloadData appendData:pushToken];
  981. [self sendPayloadWithType:PLTYPE_PUSH_NOTIFICATION_TOKEN data:payloadData];
  982. DDLogInfo(@"Sending VoIP push notification token");
  983. uint8_t voIPPushTokenType;
  984. #ifdef DEBUG
  985. voIPPushTokenType = PUSHTOKEN_TYPE_APPLE_SANDBOX;
  986. #else
  987. voIPPushTokenType = PUSHTOKEN_TYPE_APPLE_PROD;
  988. #endif
  989. NSMutableData *voipPayloadData = [NSMutableData dataWithBytes:&voIPPushTokenType length:1];
  990. NSData *voipPushToken = [[NaClCrypto sharedCrypto] zeroBytes:32];
  991. [voipPayloadData appendData:voipPushToken];
  992. [self sendPayloadWithType:PLTYPE_VOIP_PUSH_NOTIFICATION_TOKEN data:voipPayloadData];
  993. [self disconnect];
  994. }
  995. /* destroy temporary keys, as we cannot reuse them for the new identity */
  996. dispatch_async(queue, ^{
  997. clientTempKeyPub = nil;
  998. clientTempKeySec = nil;
  999. });
  1000. /* also flush the queue so that messages stuck in it don't later cause problems
  1001. because they have the wrong from identity */
  1002. [[MessageQueue sharedMessageQueue] flush];
  1003. }
  1004. @end