FIRMessagingSecureSocket.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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 <Protobuf/GPBMessage.h>
  18. #import <Protobuf/GPBCodedOutputStream.h>
  19. #import <Protobuf/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. [self openStream:self.outStream isVOIPStream:isVOIPSocket];
  111. [self openStream:self.inStream isVOIPStream:isVOIPSocket];
  112. }
  113. - (void)disconnect {
  114. if (self.state == kFIRMessagingSecureSocketClosing) {
  115. return;
  116. }
  117. if (!self.inStream && !self.outStream) {
  118. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket002,
  119. @"The socket is not open or already closed.");
  120. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketClosed || self.state == kFIRMessagingSecureSocketNotOpen,
  121. @"Socket is already disconnected.");
  122. return;
  123. }
  124. self.state = kFIRMessagingSecureSocketClosing;
  125. if (self.inStream) {
  126. [self closeStream:self.inStream];
  127. self.inStream = nil;
  128. }
  129. if (self.outStream) {
  130. [self closeStream:self.outStream];
  131. self.outStream = nil;
  132. }
  133. self.state = kFIRMessagingSecureSocketClosed;
  134. [self.delegate didDisconnectWithSecureSocket:self];
  135. }
  136. - (void)sendData:(NSData *)data withTag:(int8_t)tag rmqId:(NSString *)rmqId {
  137. [self.packetQueue push:[FIRMessagingPacket packetWithTag:tag rmqId:rmqId data:data]];
  138. if ([self.outStream hasSpaceAvailable]) {
  139. [self performWrite];
  140. }
  141. }
  142. #pragma mark - NSStreamDelegate
  143. - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
  144. switch (eventCode) {
  145. case NSStreamEventHasBytesAvailable:
  146. if (self.state != kFIRMessagingSecureSocketOpen) {
  147. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket003,
  148. @"Try to read from socket that is not opened");
  149. return;
  150. }
  151. _FIRMessagingDevAssert(stream == self.inStream, @"Incorrect stream");
  152. if (![self performRead]) {
  153. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket004,
  154. @"Error occurred when reading incoming stream");
  155. [self disconnect];
  156. }
  157. break;
  158. case NSStreamEventEndEncountered:
  159. FIRMessagingLoggerDebug(
  160. kFIRMessagingMessageCodeSecureSocket005, @"%@ end encountered",
  161. stream == self.inStream
  162. ? @"Input stream"
  163. : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
  164. [self disconnect];
  165. break;
  166. case NSStreamEventOpenCompleted:
  167. if (stream == self.inStream) {
  168. self.isInStreamOpen = YES;
  169. } else if (stream == self.outStream) {
  170. self.isOutStreamOpen = YES;
  171. }
  172. if (self.isInStreamOpen && self.isOutStreamOpen) {
  173. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket006,
  174. @"Secure socket to FIRMessaging service opened");
  175. self.state = kFIRMessagingSecureSocketOpen;
  176. [self.delegate secureSocketDidConnect:self];
  177. }
  178. break;
  179. case NSStreamEventErrorOccurred: {
  180. FIRMessagingLoggerDebug(
  181. kFIRMessagingMessageCodeSecureSocket007, @"%@ error occurred",
  182. stream == self.inStream
  183. ? @"Input stream"
  184. : (stream == self.outStream ? @"Output stream" : @"Unknown stream"));
  185. [self disconnect];
  186. break;
  187. }
  188. case NSStreamEventHasSpaceAvailable:
  189. if (self.state != kFIRMessagingSecureSocketOpen) {
  190. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket008,
  191. @"Try to write to socket that is not opened");
  192. return;
  193. }
  194. _FIRMessagingDevAssert(stream == self.outStream, @"Incorrect stream");
  195. [self performWrite];
  196. break;
  197. default:
  198. break;
  199. }
  200. }
  201. #pragma mark - Private
  202. - (void)openStream:(NSStream *)stream isVOIPStream:(BOOL)isVOIPStream {
  203. _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
  204. _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
  205. if (stream) {
  206. _FIRMessagingDevAssert([stream streamStatus] == NSStreamStatusNotOpen, @"Stream already open");
  207. if ([stream streamStatus] != NSStreamStatusNotOpen) {
  208. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket009,
  209. @"stream should not be open.");
  210. return;
  211. }
  212. [stream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
  213. forKey:NSStreamSocketSecurityLevelKey];
  214. if (isVOIPStream) {
  215. [stream setProperty:NSStreamNetworkServiceTypeVoIP
  216. forKey:NSStreamNetworkServiceType];
  217. }
  218. stream.delegate = self;
  219. [stream scheduleInRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
  220. [stream open];
  221. }
  222. }
  223. - (void)closeStream:(NSStream *)stream {
  224. _FIRMessagingDevAssert(stream != nil, @"Invalid stream");
  225. _FIRMessagingDevAssert(self.runLoop != nil, @"Invalid runloop");
  226. if (stream) {
  227. [stream close];
  228. [stream removeFromRunLoop:self.runLoop forMode:NSDefaultRunLoopMode];
  229. stream.delegate = nil;
  230. }
  231. }
  232. - (BOOL)performRead {
  233. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Socket should be open");
  234. if (!self.isVersionReceived) {
  235. self.isVersionReceived = YES;
  236. uint8_t versionByte = 0;
  237. NSInteger bytesRead = [self.inStream read:&versionByte maxLength:sizeof(uint8_t)];
  238. if (bytesRead != sizeof(uint8_t) || kVersion != versionByte) {
  239. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket010,
  240. @"Version do not match. Received %d, Expecting %d", versionByte,
  241. kVersion);
  242. return NO;
  243. }
  244. }
  245. while (YES) {
  246. BOOL isInputBufferValid = [self.inputBuffer length] > 0;
  247. _FIRMessagingDevAssert(isInputBufferValid,
  248. @"Invalid input buffer size %lu. Used bytes length %lu, buffer content: %@",
  249. _FIRMessaging_UL([self.inputBuffer length]),
  250. _FIRMessaging_UL(self.inputBufferLength),
  251. self.inputBuffer);
  252. if (!isInputBufferValid) {
  253. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket011,
  254. @"Input buffer is not valid.");
  255. return NO;
  256. }
  257. if (![self.inStream hasBytesAvailable]) {
  258. break;
  259. }
  260. // try to read more data
  261. uint8_t *unusedBufferPtr = (uint8_t *)self.inputBuffer.mutableBytes + self.inputBufferLength;
  262. NSUInteger unusedBufferLength = [self.inputBuffer length] - self.inputBufferLength;
  263. NSInteger bytesRead = [self.inStream read:unusedBufferPtr maxLength:unusedBufferLength];
  264. if (bytesRead <= 0) {
  265. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket012,
  266. @"Failed to read input stream. Bytes read %ld, Used buffer size %lu, "
  267. @"Unused buffer size %lu",
  268. _FIRMessaging_UL(bytesRead), _FIRMessaging_UL(self.inputBufferLength),
  269. _FIRMessaging_UL(unusedBufferLength));
  270. break;
  271. }
  272. // did successfully read some more data
  273. self.inputBufferLength += (NSUInteger)bytesRead;
  274. if ([self.inputBuffer length] <= self.inputBufferLength) {
  275. // shouldn't be reading more than 1MB of data in one go
  276. if ([self.inputBuffer length] + kBufferLengthIncrement > kMaxBufferLength) {
  277. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket013,
  278. @"Input buffer exceed 1M, disconnect socket");
  279. return NO;
  280. }
  281. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket014,
  282. @"Input buffer limit exceeded. Used input buffer size %lu, "
  283. @"Total input buffer size %lu. No unused buffer left. "
  284. @"Increase buffer size.",
  285. _FIRMessaging_UL(self.inputBufferLength),
  286. _FIRMessaging_UL([self.inputBuffer length]));
  287. [self.inputBuffer increaseLengthBy:kBufferLengthIncrement];
  288. _FIRMessagingDevAssert([self.inputBuffer length] > self.inputBufferLength, @"Invalid buffer size");
  289. }
  290. while (self.inputBufferLength > 0 && [self.inputBuffer length] > 0) {
  291. _FIRMessagingDevAssert([self.inputBuffer length] >= self.inputBufferLength,
  292. @"Buffer longer than length");
  293. NSRange inputRange = NSMakeRange(0, self.inputBufferLength);
  294. size_t protoBytes = 0;
  295. // read the actual proto data coming in
  296. FIRMessagingSecureSocketReadResult readResult =
  297. [self processCurrentInputBuffer:[self.inputBuffer subdataWithRange:inputRange]
  298. outOffset:&protoBytes];
  299. // Corrupt data encountered, stop processing.
  300. if (readResult == kFIRMessagingSecureSocketReadResultCorrupt) {
  301. return NO;
  302. // Incomplete data, keep trying to read by loading more from the stream.
  303. } else if (readResult == kFIRMessagingSecureSocketReadResultIncomplete) {
  304. break;
  305. }
  306. _FIRMessagingDevAssert(self.inputBufferLength >= protoBytes, @"More bytes than buffer can handle");
  307. // we have read (0, protoBytes) of data in the inputBuffer
  308. if (protoBytes == self.inputBufferLength) {
  309. // did completely read the buffer data can be reset for further processing
  310. self.inputBufferLength = 0;
  311. } else {
  312. // delete processed bytes while maintaining the buffer size.
  313. NSUInteger prevLength __unused = [self.inputBuffer length];
  314. // delete the processed bytes
  315. [self.inputBuffer replaceBytesInRange:NSMakeRange(0, protoBytes) withBytes:NULL length:0];
  316. // reallocate more data
  317. [self.inputBuffer increaseLengthBy:protoBytes];
  318. _FIRMessagingDevAssert([self.inputBuffer length] == prevLength,
  319. @"Invalid input buffer size %lu. Used bytes length %lu, "
  320. @"buffer content: %@",
  321. _FIRMessaging_UL([self.inputBuffer length]),
  322. _FIRMessaging_UL(self.inputBufferLength),
  323. self.inputBuffer);
  324. self.inputBufferLength -= protoBytes;
  325. }
  326. }
  327. }
  328. return YES;
  329. }
  330. - (FIRMessagingSecureSocketReadResult)processCurrentInputBuffer:(NSData *)readData
  331. outOffset:(size_t *)outOffset {
  332. *outOffset = 0;
  333. FIRMessagingCodedInputStream *input = [[FIRMessagingCodedInputStream alloc] initWithData:readData];
  334. int8_t rawTag;
  335. if (![input readTag:&rawTag]) {
  336. return kFIRMessagingSecureSocketReadResultIncomplete;
  337. }
  338. int32_t length;
  339. if (![input readLength:&length]) {
  340. return kFIRMessagingSecureSocketReadResultIncomplete;
  341. }
  342. // NOTE tag can be zero for |HeartbeatPing|, and length can be zero for |Close| proto
  343. _FIRMessagingDevAssert(rawTag >= 0 && length >= 0, @"Invalid tag or length");
  344. if (rawTag < 0 || length < 0) {
  345. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket015, @"Buffer data corrupted.");
  346. return kFIRMessagingSecureSocketReadResultCorrupt;
  347. }
  348. NSData *data = [input readDataWithLength:(uint32_t)length];
  349. if (data == nil) {
  350. FIRMessagingLoggerDebug(kFIRMessagingMessageCodeSecureSocket016,
  351. @"Incomplete data, buffered data length %ld, expected length %d",
  352. _FIRMessaging_UL(self.inputBufferLength), length);
  353. return kFIRMessagingSecureSocketReadResultIncomplete;
  354. }
  355. [self.delegate secureSocket:self didReceiveData:data withTag:rawTag];
  356. *outOffset = input.offset;
  357. return kFIRMessagingSecureSocketReadResultSuccess;
  358. }
  359. - (void)performWrite {
  360. _FIRMessagingDevAssert(self.state == kFIRMessagingSecureSocketOpen, @"Invalid socket state");
  361. if (!self.isVersionSent) {
  362. self.isVersionSent = YES;
  363. uint8_t versionByte = kVersion;
  364. [self.outStream write:&versionByte maxLength:sizeof(uint8_t)];
  365. }
  366. while (!self.packetQueue.isEmpty && self.outStream.hasSpaceAvailable) {
  367. if (self.outputBuffer.length == 0) {
  368. // serialize new packets only when the output buffer is flushed.
  369. FIRMessagingPacket *packet = [self.packetQueue pop];
  370. self.currentRmqIdBeingSent = packet.rmqId;
  371. self.currentProtoTypeBeingSent = packet.tag;
  372. NSUInteger length = SerializedSize(packet.tag) +
  373. SerializedSize((int)packet.data.length) + packet.data.length;
  374. self.outputBuffer = [NSMutableData dataWithLength:length];
  375. GPBCodedOutputStream *output = [GPBCodedOutputStream streamWithData:self.outputBuffer];
  376. [output writeRawVarint32:packet.tag];
  377. [output writeBytesNoTag:packet.data];
  378. self.outputBufferLength = 0;
  379. }
  380. // flush the output buffer.
  381. NSInteger written = [self.outStream write:self.outputBuffer.bytes + self.outputBufferLength
  382. maxLength:self.outputBuffer.length - self.outputBufferLength];
  383. if (written <= 0) {
  384. continue;
  385. }
  386. self.outputBufferLength += (NSUInteger)written;
  387. if (self.outputBufferLength >= self.outputBuffer.length) {
  388. self.outputBufferLength = 0;
  389. self.outputBuffer = nil;
  390. [self.delegate secureSocket:self
  391. didSendProtoWithTag:self.currentProtoTypeBeingSent
  392. rmqId:self.currentRmqIdBeingSent];
  393. self.currentRmqIdBeingSent = nil;
  394. self.currentProtoTypeBeingSent = kInvalidTag;
  395. }
  396. }
  397. }
  398. @end