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.

409 lines
15 KiB

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