+/* Secrets table.
+ *
+ * The server doesn't want to maintain state for each client. Instead, we
+ * generate a global secret, and derive per-client secrets from it. A secret
+ * needs to have an expiry time (at which point we won't use it for new
+ * requests) and a deletion time (at which point we just forget that it ever
+ * existed). This lets us roll over to a new secret without leaving existing
+ * clients completely in the lurch.
+ *
+ * Secrets are kept in a linked list, ordered by expiry time. At any given
+ * time there is at most one unexpired secret (because we only make a new one
+ * when the old one expires).
+ */
+
+struct secret {
+ struct secret *next;
+ uint32 seq;
+ time_t t_exp, t_del;
+ octet x[32];
+};
+static struct secret *secrets = 0, *live_secret = 0;
+static uint32 next_secret_seq = 0;
+#define T_SECEXP 30
+#define T_SECDEL 45
+
+static void kill_dead_secrets(void)
+{
+ struct secret *s = secrets, *ss;
+
+ for (s = secrets; s && s->t_del <= now; s = ss) {
+ ss = s->next;
+ DESTROY(s);
+ }
+ secrets = 0;
+ if (!s) live_secret = 0;
+}
+
+static struct secret *find_secret(uint32 seq)
+{
+ struct secret *s;
+
+ kill_dead_secrets();
+ for (s = secrets; s; s = s->next)
+ if (s->seq == seq) return (s);
+ return (0);
+}
+
+static struct secret *fresh_secret(void)
+{
+ struct secret *s;
+
+ if (live_secret && live_secret->t_exp > now) return (live_secret);
+ kill_dead_secrets();
+
+ s = CREATE(struct secret);
+ s->seq = next_secret_seq++;
+ s->next = 0;
+ rand_get(RAND_GLOBAL, s->x, sizeof(s->x));
+ s->t_exp = now + T_SECEXP; s->t_del = now + T_SECDEL;
+ if (live_secret) live_secret->next = s;
+ else secrets = s;
+ live_secret = s;
+ return (s);
+}
+