1 /* Copyright (c) 2012-2013, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
7 * \brief Self-scrubbing replay cache for rendservice.c
10 #define REPLAYCACHE_PRIVATE
13 #include "replaycache.h"
15 /** Free the replaycache r and all of its entries.
19 replaycache_free(replaycache_t
*r
)
22 log_info(LD_BUG
, "replaycache_free() called on NULL");
26 if (r
->digests_seen
) digestmap_free(r
->digests_seen
, tor_free_
);
31 /** Allocate a new, empty replay detection cache, where horizon is the time
32 * for entries to age out and interval is the time after which the cache
33 * should be scrubbed for old entries.
37 replaycache_new(time_t horizon
, time_t interval
)
39 replaycache_t
*r
= NULL
;
42 log_info(LD_BUG
, "replaycache_new() called with negative"
43 " horizon parameter");
48 log_info(LD_BUG
, "replaycache_new() called with negative interval"
53 r
= tor_malloc(sizeof(*r
));
54 r
->scrub_interval
= interval
;
57 r
->digests_seen
= digestmap_new();
63 /** See documentation for replaycache_add_and_test()
67 replaycache_add_and_test_internal(
68 time_t present
, replaycache_t
*r
, const void *data
, size_t len
,
72 char digest
[DIGEST_LEN
];
76 if (present
<= 0 || !r
|| !data
|| len
== 0) {
77 log_info(LD_BUG
, "replaycache_add_and_test_internal() called with stupid"
78 " parameters; please fix this.");
83 crypto_digest(digest
, (const char *)data
, len
);
86 access_time
= digestmap_get(r
->digests_seen
, digest
);
89 if (access_time
!= NULL
) {
91 * If it's far enough in the past, no hit. If the horizon is zero, we
94 if (*access_time
>= present
- r
->horizon
|| r
->horizon
== 0) {
95 /* replay cache hit, return 1 */
97 /* If we want to output an elapsed time, do so */
99 if (present
>= *access_time
) {
100 *elapsed
= present
- *access_time
;
102 /* We shouldn't really be seeing hits from the future, but... */
108 * If it's ahead of the cached time, update
110 if (*access_time
< present
) {
111 *access_time
= present
;
114 /* No, so no hit and update the digest map with the current time */
115 access_time
= tor_malloc(sizeof(*access_time
));
116 *access_time
= present
;
117 digestmap_set(r
->digests_seen
, digest
, access_time
);
120 /* now scrub the cache if it's time */
121 replaycache_scrub_if_needed_internal(present
, r
);
127 /** See documentation for replaycache_scrub_if_needed()
131 replaycache_scrub_if_needed_internal(time_t present
, replaycache_t
*r
)
133 digestmap_iter_t
*itr
= NULL
;
139 if (!r
|| !(r
->digests_seen
)) {
140 log_info(LD_BUG
, "replaycache_scrub_if_needed_internal() called with"
141 " stupid parameters; please fix this.");
145 /* scrub time yet? (scrubbed == 0 indicates never scrubbed before) */
146 if (present
- r
->scrubbed
< r
->scrub_interval
&& r
->scrubbed
> 0) return;
148 /* if we're never expiring, don't bother scrubbing */
149 if (r
->horizon
== 0) return;
151 /* okay, scrub time */
152 itr
= digestmap_iter_init(r
->digests_seen
);
153 while (!digestmap_iter_done(itr
)) {
154 digestmap_iter_get(itr
, &digest
, &valp
);
155 access_time
= (time_t *)valp
;
157 if (*access_time
< present
- r
->horizon
) {
158 /* Advance the iterator and remove this one */
159 itr
= digestmap_iter_next_rmv(r
->digests_seen
, itr
);
160 /* Free the value removed */
161 tor_free(access_time
);
163 /* Just advance the iterator */
164 itr
= digestmap_iter_next(r
->digests_seen
, itr
);
168 /* update scrubbed timestamp */
169 if (present
> r
->scrubbed
) r
->scrubbed
= present
;
172 /** Test the buffer of length len point to by data against the replay cache r;
173 * the digest of the buffer will be added to the cache at the current time,
174 * and the function will return 1 if it was already seen within the cache's
175 * horizon, or 0 otherwise.
179 replaycache_add_and_test(replaycache_t
*r
, const void *data
, size_t len
)
181 return replaycache_add_and_test_internal(time(NULL
), r
, data
, len
, NULL
);
184 /** Like replaycache_add_and_test(), but if it's a hit also return the time
185 * elapsed since this digest was last seen.
189 replaycache_add_test_and_elapsed(
190 replaycache_t
*r
, const void *data
, size_t len
, time_t *elapsed
)
192 return replaycache_add_and_test_internal(time(NULL
), r
, data
, len
, elapsed
);
195 /** Scrub aged entries out of r if sufficiently long has elapsed since r was
200 replaycache_scrub_if_needed(replaycache_t
*r
)
202 replaycache_scrub_if_needed_internal(time(NULL
), r
);