You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

448 lines
17 KiB

6 years ago
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRMessagingSecureSocket.h"
  17. #import "GPBMessage.h"
  18. #import "GPBCodedOutputStream.h"
  19. #import "GPBUtilities.h"
  20. #import "FIRMessagingCodedInputStream.h"
  21. #import "FIRMessagingDefines.h"
  22. #import "FIRMessagingLogger.h"
  23. #import "FIRMessagingPacketQueue.h"
  24. static const NSUInteger kMaxBufferLength = 1024 * 1024; // 1M
  25. static const NSUInteger kBufferLengthIncrement = 16 * 1024; // 16k
  26. static const uint8_t kVersion = 40;
  27. static const uint8_t kInvalidTag = -1;
  28. typedef NS_ENUM(NSUInteger, FIRMessagingSecureSocketReadResult) {
  29. kFIRMessagingSecureSocketReadResultNone,
  30. kFIRMessagingSecureSocketReadResultIncomplete,
  31. kFIRMessagingSecureSocketReadResultCorrupt,
  32. kFIRMessagingSecureSocketReadResultSuccess
  33. };
  34. static int32_t LogicalRightShift32(int32_t value, int32_t spaces) {
  35. return (int32_t)((uint32_t)(value) >> spaces);
  36. }
  37. static NSUInteger SerializedSize(int32_t value) {
  38. NSUInteger bytes = 0;
  39. while (YES) {
  40. if ((value & ~0x7F) == 0) {
  41. bytes += sizeof(uint8_t);
  42. return bytes;
  43. } else {
  44. bytes += sizeof(uint8_t);
  45. value = LogicalRightShift32(value, 7);
  46. }
  47. }
  48. }
  49. @interface FIRMessagingSecureSocket() <NSStreamDelegate>
  50. @property(nonatomic, readwrite, assign) FIRMessagingSecureSocketState state;
  51. @property(nonatomic, readwrite, strong) NSInputStream *inStream;
  52. @property(nonatomic, readwrite, strong) NSOutputStream *outStream;
  53. @property(nonatomic, readwrite, strong) NSMutableData *inputBuffer;
  54. @property(nonatomic, readwrite, assign) NSUInteger inputBufferLength;
  55. @property(nonatomic, readwrite, strong) NSMutableData *outputBuffer;
  56. @property(nonatomic, readwrite, assign) NSUInteger outputBufferLength;
  57. @property(nonatomic, readwrite, strong) FIRMessagingPacketQueue *packetQueue;
  58. @property(nonatomic, readwrite, assign) BOOL isVersionSent;
  59. @property(nonatomic, readwrite, assign) BOOL isVersionReceived;
  60. @property(nonatomic, readwrite, assign) BOOL isInStreamOpen;
  61. @property(nonatomic, readwrite, assign) BOOL isOutStreamOpen;
  62. @property(nonatomic, readwrite, strong) NSRunLoop *runLoop;
  63. @property(nonatomic, readwrite, strong) NSString *currentRmqIdBeingSent;
  64. @property(nonatomic, readwrite, assign) int8_t currentProtoTypeBeingSent;
  65. @end
  66. @implementation FIRMessagingSecureSocket
  67. - (instancetype)init {
  68. self = [super init];
  69. if (self) {
  70. _state = kFIRMessagingSecureSocketNotOpen;
  71. _inputBuffer = [NSMutableData dataWithLength:kBufferLengthIncrement];
  72. _packetQueue = [[FIRMessagingPacketQueue alloc] init];
  73. _currentProtoTypeBeingSent = kInvalidTag;
  74. }
  75. return self;
  76. }
  77. - (void)dealloc {
  78. [self disconnect];
  79. }
  80. - (void)connectToHost:(NSString *)host
  81. port:(NSUInteger)port
  82. onRunLoop:(NSRunLoop *)runLoop {
  83. _FIRMessagingDevAssert(host != nil, @"Invalid host");
  84. _FIRMessagingDevAssert(runLoop != nil, @"Invalid runloop");
  85. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketNotOpen, @"Socket is already connected");
  86. if (!host || self.state != kFIRMessagingSecureSocketNotOpen) {
  87. return;
  88. }
  89. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket000,
  90. @"Opening secure socket to FIRMessaging service");
  91. self.state = kFIRMessagingSecureSocketOpening;
  92. self.runLoop = runLoop;
  93. CFReadStreamRef inputStreamRef;
  94. CFWriteStreamRef outputStreamRef;
  95. CFStreamCreatePairWithSocketToHost(NULL,
  96. (__bridge CFStringRef)host,
  97. (int)port,
  98. &inputStreamRef,
  99. &outputStreamRef);
  100. self.inStream = CFBridgingRelease(inputStreamRef);
  101. self.outStream = CFBridgingRelease(outputStreamRef);
  102. if (!self.inStream || !self.outStream) {
  103. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket001,
  104. @"Failed to initialize socket.");
  105. return;
  106. }
  107. self.isInStreamOpen = NO;
  108. self.isOutStreamOpen = NO;
  109. BOOL isVOIPSocket = NO;
  110. #if FIRMessaging_PROBER
  111. isVOIPSocket = YES;
  112. #endif
  113. [self openStream:self.outStream isVOIPStream:isVOIPSocket];
  114. [self openStream:self.inStream isVOIPStream:isVOIPSocket];
  115. }
  116. - (void)disconnect {
  117. if (self.state == kFIRMessagingSecureSocketClosing) {
  118. return;
  119. }
  120. if (!self.inStream && !self.outStream) {
  121. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket002,
  122. @"The socket is not open or already closed.");
  123. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketClosed || self.state == kFIRMessagingSecureSocketNotOpen,
  124. @"Socket is already disconnected.");
  125. return;
  126. }
  127. self.state = kFIRMessagingSecureSocketClosing;
  128. if (self.inStream) {
  129. [self closeStream:self.inStream];
  130. self.inStream = nil;
  131. }
  132. if (self.outStream) {
  133. [self closeStream:self.outStream];
  134. self.outStream = nil;
  135. }
  136. self.state = kFIRMessagingSecureSocketClosed;
  137. [self.delegate didDisconnectWithSecureSocket:self];
  138. }
  139. - (void)sendData:(NSData *)data withTag:(int8_t)tag rmqId:(NSString *)rmqId {
  140. [self.packetQueue push:[FIRMessagingPacket packetWithTag:tag rmqId:rmqId data:data]];
  141. if ([self.outStream hasSpaceAvailable]) {
  142. [self performWrite];
  143. }
  144. }
  145. #pragma mark - NSStreamDelegate
  146. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
  147. switch (eventCode) {
  148. case NSStreamEventHasBytesAvailable:
  149. if (self.state != kFIRMessagingSecureSocketOpen) {
  150. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket003,
  151. @"Try to read from socket that is not opened");
  152. return;
  153. }
  154. _FIRMessagingDevAssert(stream == self.inStream, @"Incorrect stream");
  155. if (![self performRead]) {
  156. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket004,
  157. @"Error occured when reading incoming stream");
  158. [self disconnect];
  159. }
  160. break;
  161. case NSStreamEventEndEncountered:
  162. FIRMessagingLoggerDebug(
  163. kFIRMessagingMessageCodeSecureSocket005, @"%@ end encountered",
  164. stream == self.inStream
  165. ? @"Input stream"
  166. : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
  167. [self disconnect];
  168. break;
  169. case NSStreamEventOpenCompleted:
  170. if (stream == self.inStream) {
  171. self.isInStreamOpen = YES;
  172. } else if (stream == self.outStream) {
  173. self.isOutStreamOpen = YES;
  174. }
  175. if (self.isInStreamOpen && self.isOutStreamOpen) {
  176. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket006,
  177. @"Secure socket to FIRMessaging service opened");
  178. self.state = kFIRMessagingSecureSocketOpen;
  179. [self.delegate secureSocketDidConnect:self];
  180. }
  181. break;
  182. case NSStreamEventErrorOccurred: {
  183. FIRMessagingLoggerDebug(
  184. kFIRMessagingMessageCodeSecureSocket007, @"%@ error occurred",
  185. stream == self.inStream
  186. ? @"Input stream"
  187. : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
  188. [self disconnect];
  189. break;
  190. }
  191. case NSStreamEventHasSpaceAvailable:
  192. if (self.state != kFIRMessagingSecureSocketOpen) {
  193. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket008,
  194. @"Try to write to socket that is not opened");
  195. return;
  196. }
  197. _FIRMessagingDevAssert(stream == self.outStream, @"Incorrect stream");
  198. [self performWrite];
  199. break;
  200. default:
  201. break;
  202. }
  203. }
  204. #pragma mark - Private
  205. - (void)openStream:(NSStream *)stream isVOIPStream:(BOOL)isVOIPStream {
  206. _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
  207. _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
  208. if (stream) {
  209. _FIRMessagingDevAssert([stream streamStatus] == NSStreamStatusNotOpen, @"Stream already open");
  210. if ([stream streamStatus] != NSStreamStatusNotOpen) {
  211. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket009,
  212. @"stream should not be open.");
  213. return;
  214. }
  215. [stream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
  216. forKey:NSStreamSocketSecurityLevelKey];
  217. if (isVOIPStream) {
  218. [stream setProperty:NSStreamNetworkServiceTypeVoIP
  219. forKey:NSStreamNetworkServiceType];
  220. }
  221. stream.delegate = self;
  222. [stream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
  223. [stream open];
  224. }
  225. }
  226. - (void)closeStream:(NSStream *)stream {
  227. _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
  228. _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
  229. if (stream) {
  230. [stream close];
  231. [stream removeFromRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
  232. stream.delegate = nil;
  233. }
  234. }
  235. - (BOOL)performRead {
  236. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Socket should be open");
  237. if (!self.isVersionReceived) {
  238. self.isVersionReceived = YES;
  239. uint8_t versionByte = 0;
  240. NSInteger bytesRead = [self.inStream read:&versionByte maxLength:sizeof(uint8_t)];
  241. if (bytesRead != sizeof(uint8_t) || kVersion != versionByte) {
  242. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket010,
  243. @"Version do not match. Received %d, Expecting %d", versionByte,
  244. kVersion);
  245. return NO;
  246. }
  247. }
  248. while (YES) {
  249. BOOL isInputBufferValid = [self.inputBuffer length] > 0;
  250. _FIRMessagingDevAssert(isInputBufferValid,
  251. @"Invalid input buffer size %lu. Used bytes length %lu, buffer content: %@",
  252. _FIRMessaging_UL([self.inputBuffer length]),
  253. _FIRMessaging_UL(self.inputBufferLength),
  254. self.inputBuffer);
  255. if (!isInputBufferValid) {
  256. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket011,
  257. @"Input buffer is not valid.");
  258. return NO;
  259. }
  260. if (![self.inStream hasBytesAvailable]) {
  261. break;
  262. }
  263. // try to read more data
  264. uint8_t *unusedBufferPtr = (uint8_t *)self.inputBuffer.mutableBytes + self.inputBufferLength;
  265. NSUInteger unusedBufferLength = [self.inputBuffer length] - self.inputBufferLength;
  266. NSInteger bytesRead = [self.inStream read:unusedBufferPtr maxLength:unusedBufferLength];
  267. if (bytesRead <= 0) {
  268. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket012,
  269. @"Failed to read input stream. Bytes read %ld, Used buffer size %lu, "
  270. @"Unused buffer size %lu",
  271. _FIRMessaging_UL(bytesRead), _FIRMessaging_UL(self.inputBufferLength),
  272. _FIRMessaging_UL(unusedBufferLength));
  273. break;
  274. }
  275. // did successfully read some more data
  276. self.inputBufferLength += (NSUInteger)bytesRead;
  277. if ([self.inputBuffer length] <= self.inputBufferLength) {
  278. // shouldn't be reading more than 1MB of data in one go
  279. if ([self.inputBuffer length] + kBufferLengthIncrement > kMaxBufferLength) {
  280. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket013,
  281. @"Input buffer exceed 1M, disconnect socket");
  282. return NO;
  283. }
  284. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket014,
  285. @"Input buffer limit exceeded. Used input buffer size %lu, "
  286. @"Total input buffer size %lu. No unused buffer left. "
  287. @"Increase buffer size.",
  288. _FIRMessaging_UL(self.inputBufferLength),
  289. _FIRMessaging_UL([self.inputBuffer length]));
  290. [self.inputBuffer increaseLengthBy:kBufferLengthIncrement];
  291. _FIRMessagingDevAssert([self.inputBuffer length] > self.inputBufferLength, @"Invalid buffer size");
  292. }
  293. while (self.inputBufferLength > 0 && [self.inputBuffer length] > 0) {
  294. _FIRMessagingDevAssert([self.inputBuffer length] >= self.inputBufferLength,
  295. @"Buffer longer than length");
  296. NSRange inputRange = NSMakeRange(0, self.inputBufferLength);
  297. size_t protoBytes = 0;
  298. // read the actual proto data coming in
  299. FIRMessagingSecureSocketReadResult readResult =
  300. [self processCurrentInputBuffer:[self.inputBuffer subdataWithRange:inputRange]
  301. outOffset:&protoBytes];
  302. // Corrupt data encountered, stop processing.
  303. if (readResult == kFIRMessagingSecureSocketReadResultCorrupt) {
  304. return NO;
  305. // Incomplete data, keep trying to read by loading more from the stream.
  306. } else if (readResult == kFIRMessagingSecureSocketReadResultIncomplete) {
  307. break;
  308. }
  309. _FIRMessagingDevAssert(self.inputBufferLength >= protoBytes, @"More bytes than buffer can handle");
  310. // we have read (0, protoBytes) of data in the inputBuffer
  311. if (protoBytes == self.inputBufferLength) {
  312. // did completely read the buffer data can be reset for further processing
  313. self.inputBufferLength = 0;
  314. } else {
  315. // delete processed bytes while maintaining the buffer size.
  316. NSUInteger prevLength __unused = [self.inputBuffer length];
  317. // delete the processed bytes
  318. [self.inputBuffer replaceBytesInRange:NSMakeRange(0, protoBytes) withBytes:NULL length:0];
  319. // reallocate more data
  320. [self.inputBuffer increaseLengthBy:protoBytes];
  321. _FIRMessagingDevAssert([self.inputBuffer length] == prevLength,
  322. @"Invalid input buffer size %lu. Used bytes length %lu, "
  323. @"buffer content: %@",
  324. _FIRMessaging_UL([self.inputBuffer length]),
  325. _FIRMessaging_UL(self.inputBufferLength),
  326. self.inputBuffer);
  327. self.inputBufferLength -= protoBytes;
  328. }
  329. }
  330. }
  331. return YES;
  332. }
  333. - (FIRMessagingSecureSocketReadResult)processCurrentInputBuffer:(NSData *)readData
  334. outOffset:(size_t *)outOffset {
  335. *outOffset = 0;
  336. FIRMessagingCodedInputStream *input = [[FIRMessagingCodedInputStream alloc] initWithData:readData];
  337. int8_t rawTag;
  338. if (![input readTag:&rawTag]) {
  339. return kFIRMessagingSecureSocketReadResultIncomplete;
  340. }
  341. int32_t length;
  342. if (![input readLength:&length]) {
  343. return kFIRMessagingSecureSocketReadResultIncomplete;
  344. }
  345. // NOTE tag can be zero for |HeartbeatPing|, and length can be zero for |Close| proto
  346. _FIRMessagingDevAssert(rawTag >= 0 && length >= 0, @"Invalid tag or length");
  347. if (rawTag < 0 || length < 0) {
  348. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket015, @"Buffer data corrupted.");
  349. return kFIRMessagingSecureSocketReadResultCorrupt;
  350. }
  351. NSData *data = [input readDataWithLength:(uint32_t)length];
  352. if (data == nil) {
  353. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket016,
  354. @"Incomplete data, buffered data length %ld, expected length %d",
  355. _FIRMessaging_UL(self.inputBufferLength), length);
  356. return kFIRMessagingSecureSocketReadResultIncomplete;
  357. }
  358. [self.delegate secureSocket:self didReceiveData:data withTag:rawTag];
  359. *outOffset = input.offset;
  360. return kFIRMessagingSecureSocketReadResultSuccess;
  361. }
  362. - (void)performWrite {
  363. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Invalid socket state");
  364. if (!self.isVersionSent) {
  365. self.isVersionSent = YES;
  366. uint8_t versionByte = kVersion;
  367. [self.outStream write:&versionByte maxLength:sizeof(uint8_t)];
  368. }
  369. while (!self.packetQueue.isEmpty && self.outStream.hasSpaceAvailable) {
  370. if (self.outputBuffer.length == 0) {
  371. // serialize new packets only when the output buffer is flushed.
  372. FIRMessagingPacket *packet = [self.packetQueue pop];
  373. self.currentRmqIdBeingSent = packet.rmqId;
  374. self.currentProtoTypeBeingSent = packet.tag;
  375. NSUInteger length = SerializedSize(packet.tag) +
  376. SerializedSize((int)packet.data.length) + packet.data.length;
  377. self.outputBuffer = [NSMutableData dataWithLength:length];
  378. GPBCodedOutputStream *output = [GPBCodedOutputStream streamWithData:self.outputBuffer];
  379. [output writeRawVarint32:packet.tag];
  380. [output writeBytesNoTag:packet.data];
  381. self.outputBufferLength = 0;
  382. }
  383. // flush the output buffer.
  384. NSInteger written = [self.outStream write:self.outputBuffer.bytes + self.outputBufferLength
  385. maxLength:self.outputBuffer.length - self.outputBufferLength];
  386. if (written <= 0) {
  387. continue;
  388. }
  389. self.outputBufferLength += (NSUInteger)written;
  390. if (self.outputBufferLength >= self.outputBuffer.length) {
  391. self.outputBufferLength = 0;
  392. self.outputBuffer = nil;
  393. [self.delegate secureSocket:self
  394. didSendProtoWithTag:self.currentProtoTypeBeingSent
  395. rmqId:self.currentRmqIdBeingSent];
  396. self.currentRmqIdBeingSent = nil;
  397. self.currentProtoTypeBeingSent = kInvalidTag;
  398. }
  399. }
  400. }
  401. @end