TSKReportsRateLimiter.m 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /*
  2. TSKReportsRateLimiter.m
  3. TrustKit
  4. Copyright 2015 The TrustKit Project Authors
  5. Licensed under the MIT license, see associated LICENSE file for terms.
  6. See AUTHORS file for the list of project authors.
  7. */
  8. #import "TSKReportsRateLimiter.h"
  9. #import "reporting_utils.h"
  10. static const NSTimeInterval kIntervalBetweenReportsCacheReset = 3600 * 24;
  11. @interface TSKReportsRateLimiter ()
  12. /** Cache to rate-limit the number of pin failure reports that get sent */
  13. @property (nonatomic) NSMutableSet *reportsCache;
  14. /** We reset the reports cache every 24 hours to ensure identical reports are only sent once per day */
  15. @property (nonatomic) NSDate *lastReportsCacheResetDate;
  16. /** Concurrent queue for multi-reader, single-writer to the reports cache using dispatch barriers */
  17. @property (nonatomic) dispatch_queue_t reportsCacheQueue;
  18. @end
  19. @implementation TSKReportsRateLimiter
  20. - (instancetype)init
  21. {
  22. self = [super init];
  23. if (self) {
  24. // Initialize all the internal state for rate-limiting report uploads
  25. _reportsCache = [NSMutableSet set];
  26. _lastReportsCacheResetDate = [NSDate date];
  27. _reportsCacheQueue = dispatch_queue_create("TSKReportsRateLimiter", DISPATCH_QUEUE_SERIAL);
  28. }
  29. return self;
  30. }
  31. - (BOOL)shouldRateLimitReport:(TSKPinFailureReport *)report
  32. {
  33. NSParameterAssert(report);
  34. // Check if we need to clear the reports cache for rate-limiting
  35. NSTimeInterval secondsSinceCacheReset = -[self.lastReportsCacheResetDate timeIntervalSinceNow];
  36. // Create an array containg the gist of the pin failure report; do not include the dates
  37. NSArray *pinFailureInfo = @[ report.notedHostname,
  38. report.hostname,
  39. report.port,
  40. report.validatedCertificateChain,
  41. report.knownPins,
  42. @(report.validationResult) ];
  43. __block BOOL shouldRateLimitReport = NO;
  44. __weak typeof(self) weakSelf = self;
  45. dispatch_sync(self.reportsCacheQueue, ^{
  46. typeof(self) strongSelf = weakSelf;
  47. if (secondsSinceCacheReset > kIntervalBetweenReportsCacheReset)
  48. {
  49. // Reset the cache
  50. [strongSelf.reportsCache removeAllObjects];
  51. strongSelf.lastReportsCacheResetDate = [NSDate date];
  52. }
  53. // Check if the exact same report has already been sent recently
  54. shouldRateLimitReport = [strongSelf.reportsCache containsObject:pinFailureInfo];
  55. if (shouldRateLimitReport == NO)
  56. {
  57. // An identical report has NOT been sent recently
  58. // Add this report to the cache for rate-limiting
  59. [strongSelf.reportsCache addObject:pinFailureInfo];
  60. }
  61. });
  62. return shouldRateLimitReport;
  63. }
  64. - (void)setLastReportsCacheResetDate:(NSDate *)lastReportsCacheResetDate
  65. {
  66. NSParameterAssert(lastReportsCacheResetDate);
  67. _lastReportsCacheResetDate = lastReportsCacheResetDate;
  68. }
  69. @end