FIRMessagingSecureSocket.m 15 KB

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