Drop retransmits while processing requests
[krb5.git] / src / kdc / replay.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/replay.c - Replay lookaside cache for the KDC, to avoid extra work */
3 /*
4  * Copyright 1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "kdc_util.h"
29 #include "extern.h"
30
31 #ifndef NOCACHE
32
33 typedef struct _krb5_kdc_replay_ent {
34     struct _krb5_kdc_replay_ent *next;
35     int num_hits;
36     krb5_int32 timein;
37     krb5_data *req_packet;
38     krb5_data *reply_packet;
39 } krb5_kdc_replay_ent;
40
41 static krb5_kdc_replay_ent root_ptr = {0};
42
43 static int hits = 0;
44 static int calls = 0;
45 static int max_hits_per_entry = 0;
46 static int num_entries = 0;
47
48 #define STALE_TIME      2*60            /* two minutes */
49 #define STALE(ptr) (abs((ptr)->timein - timenow) >= STALE_TIME)
50
51 #define MATCH(ptr) (((ptr)->req_packet->length == inpkt->length) &&     \
52                     !memcmp((ptr)->req_packet->data, inpkt->data,       \
53                             inpkt->length))
54 /* XXX
55    Todo:  quench the size of the queue...
56 */
57
58 /* Removes the most recent cache entry for a given packet. */
59 void
60 kdc_remove_lookaside(krb5_context kcontext, krb5_data *inpkt)
61 {
62     register krb5_kdc_replay_ent *eptr, *last;
63
64     if (!root_ptr.next)
65         return;
66
67     for (last = &root_ptr, eptr = root_ptr.next;
68          eptr;
69          last = eptr, eptr = eptr->next) {
70         if (!MATCH(eptr))
71             continue;
72
73         last->next = eptr->next;
74         krb5_free_data(kcontext, eptr->req_packet);
75         krb5_free_data(kcontext, eptr->reply_packet);
76         free(eptr);
77         return;
78     }
79 }
80
81 /* return TRUE if outpkt is filled in with a packet to reply with,
82    FALSE if the caller should do the work */
83
84 krb5_boolean
85 kdc_check_lookaside(krb5_data *inpkt, krb5_data **outpkt)
86 {
87     krb5_int32 timenow;
88     register krb5_kdc_replay_ent *eptr, *last, *hold;
89
90     if (krb5_timeofday(kdc_context, &timenow))
91         return FALSE;
92
93     calls++;
94
95     /* search for a replay entry in the queue, possibly removing
96        stale entries while we're here */
97
98     if (root_ptr.next) {
99         for (last = &root_ptr, eptr = root_ptr.next;
100              eptr;
101              eptr = eptr->next) {
102             if (MATCH(eptr)) {
103                 eptr->num_hits++;
104                 hits++;
105
106                 if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt))
107                     return FALSE;
108                 else
109                     return TRUE;
110                 /* return here, don't bother flushing even if it is stale.
111                    if we just matched, we may get another retransmit... */
112             }
113             if (STALE(eptr)) {
114                 /* flush it and collect stats */
115                 max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits);
116                 krb5_free_data(kdc_context, eptr->req_packet);
117                 krb5_free_data(kdc_context, eptr->reply_packet);
118                 hold = eptr;
119                 last->next = eptr->next;
120                 eptr = last;
121                 free(hold);
122             } else {
123                 /* this isn't it, just move along */
124                 last = eptr;
125             }
126         }
127     }
128     return FALSE;
129 }
130
131 /* insert a request & reply into the lookaside queue.  assumes it's not
132    already there, and can fail softly due to other weird errors. */
133
134 void
135 kdc_insert_lookaside(krb5_data *inpkt, krb5_data *outpkt)
136 {
137     register krb5_kdc_replay_ent *eptr;
138     krb5_int32 timenow;
139
140     if (krb5_timeofday(kdc_context, &timenow))
141         return;
142
143     /* this is a new entry */
144     eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr));
145     if (!eptr)
146         return;
147     eptr->timein = timenow;
148     /*
149      * This is going to hurt a lot malloc()-wise due to the need to
150      * allocate memory for the krb5_data and krb5_address elements.
151      * ARGH!
152      */
153     if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) {
154         free(eptr);
155         return;
156     }
157     if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) {
158         krb5_free_data(kdc_context, eptr->req_packet);
159         free(eptr);
160         return;
161     }
162     eptr->next = root_ptr.next;
163     root_ptr.next = eptr;
164     num_entries++;
165     return;
166 }
167
168 /* frees memory associated with the lookaside queue for memory profiling */
169 void
170 kdc_free_lookaside(krb5_context kcontext)
171 {
172     register krb5_kdc_replay_ent *eptr, *last, *hold;
173     if (root_ptr.next) {
174         for (last = &root_ptr, eptr = root_ptr.next;
175              eptr; eptr = eptr->next) {
176             krb5_free_data(kcontext, eptr->req_packet);
177             krb5_free_data(kcontext, eptr->reply_packet);
178             hold = eptr;
179             last->next = eptr->next;
180             eptr = last;
181             free(hold);
182         }
183     }
184 }
185
186 #endif /* NOCACHE */