1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/threads/t_rcache.c */
3 /*
4 * Copyright (C) 2006 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 <com_err.h>
29 #include <krb5.h>
30 #include <pthread.h>
31
32 krb5_context ctx;
33 krb5_rcache rcache;
34 const char *rcname;
35 time_t end_time;
36 const char *prog;
37
38 struct tinfo {
39 time_t now;
40 unsigned long my_ctime;
41 unsigned int my_cusec;
42 unsigned int total;
43 int idx;
44 };
45
46 #define DEFAULT_N_THREADS 2
47 #define DEFAULT_INTERVAL 20 /* 5 * 60 */
48
49 int init_once = 0;
50 int n_threads = DEFAULT_N_THREADS;
51 int interval = DEFAULT_INTERVAL;
52 int *ip;
53
wait_for_tick()54 static void wait_for_tick ()
55 {
56 time_t now, next;
57 now = time(0);
58 do {
59 next = time(0);
60 } while (now == next);
61 }
62
63 /* Encrypt data into out (preallocated by the caller) with a random key. */
encrypt_data(krb5_data * data,krb5_enc_data * out)64 static krb5_error_code encrypt_data (krb5_data *data, krb5_enc_data *out)
65 {
66 krb5_keyblock kb;
67 krb5_error_code err;
68
69 err = krb5_c_make_random_key(ctx, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
70 &kb);
71 if (err)
72 return err;
73 err = krb5_c_encrypt(ctx, &kb, KRB5_KEYUSAGE_TGS_REQ_AUTH, NULL, data,
74 out);
75 krb5_free_keyblock_contents(ctx, &kb);
76 return err;
77 }
78
try_one(struct tinfo * t)79 static void try_one (struct tinfo *t)
80 {
81 krb5_error_code err;
82 char buf[256], buf2[512];
83 krb5_rcache my_rcache;
84 krb5_data d;
85 krb5_enc_data enc;
86
87 snprintf(buf, sizeof(buf), "host/all-in-one.mit.edu/%p@ATHENA.MIT.EDU",
88 buf);
89
90 /* k5_rc_store() requires a ciphertext. Create one by encrypting a dummy
91 * value in a random key. */
92 d = string2data(buf);
93 enc.ciphertext = make_data(buf2, sizeof(buf2));
94 err = encrypt_data(&d, &enc);
95 if (err != 0) {
96 const char *msg = krb5_get_error_message(ctx, err);
97 fprintf(stderr, "%s: encrypting authenticator: %s\n", prog, msg);
98 krb5_free_error_message(ctx, msg);
99 exit(1);
100 }
101
102 if (t->now != t->my_ctime) {
103 if (t->my_ctime != 0) {
104 snprintf(buf2, sizeof(buf2), "%3d: %ld %5d\n", t->idx,
105 t->my_ctime, t->my_cusec);
106 printf("%s", buf2);
107 }
108 t->my_ctime = t->now;
109 t->my_cusec = 1;
110 } else
111 t->my_cusec++;
112 if (!init_once) {
113 err = k5_rc_resolve(ctx, rcname, &my_rcache);
114 if (err) {
115 const char *msg = krb5_get_error_message(ctx, err);
116 fprintf(stderr, "%s: %s while initializing replay cache\n", prog, msg);
117 krb5_free_error_message(ctx, msg);
118 exit(1);
119 }
120 } else
121 my_rcache = rcache;
122 err = k5_rc_store(ctx, my_rcache, &enc);
123 if (err) {
124 com_err(prog, err, "storing in replay cache");
125 exit(1);
126 }
127 if (!init_once)
128 k5_rc_close(ctx, my_rcache);
129 }
130
run_a_loop(void * x)131 static void *run_a_loop (void *x)
132 {
133 struct tinfo t = { 0 };
134
135 t.now = time(0);
136 t.idx = *(int *)x;
137 while (t.now != time(0))
138 ;
139 t.now = time(0);
140 while (t.now < end_time) {
141 t.now = time(0);
142 try_one(&t);
143 t.total++;
144 }
145 *(int*)x = t.total;
146 return 0;
147 }
148
usage(void)149 static void usage(void)
150 {
151 fprintf (stderr, "usage: %s [ options ] rcname\n", prog);
152 fprintf (stderr, "options:\n");
153 fprintf (stderr, "\t-1\tcreate one rcache handle for process\n");
154 fprintf (stderr, "\t-t N\tnumber of threads to create (default: %d)\n",
155 DEFAULT_N_THREADS);
156 fprintf (stderr,
157 "\t-i N\tinterval to run test over, in seconds (default: %d)\n",
158 DEFAULT_INTERVAL);
159 exit(1);
160 }
161
162 static const char optstring[] = "1t:i:";
163
process_options(int argc,char * argv[])164 static void process_options (int argc, char *argv[])
165 {
166 int c;
167
168 prog = argv[0];
169 while ((c = getopt(argc, argv, optstring)) != -1) {
170 switch (c) {
171 case '?':
172 case ':':
173 default:
174 usage ();
175 case '1':
176 init_once = 1;
177 break;
178 case 't':
179 n_threads = atoi (optarg);
180 if (n_threads < 1 || n_threads > 10000)
181 usage ();
182 break;
183 case 'i':
184 interval = atoi (optarg);
185 if (interval < 2 || n_threads > 100000)
186 usage ();
187 break;
188 }
189 }
190
191 argc -= optind;
192 argv += optind;
193 if (argc != 1)
194 usage ();
195 rcname = argv[0];
196 }
197
main(int argc,char * argv[])198 int main (int argc, char *argv[])
199 {
200 krb5_error_code err;
201 int i;
202 unsigned long sum;
203
204 process_options (argc, argv);
205 err = krb5_init_context(&ctx);
206 if (err) {
207 com_err(prog, err, "initializing context");
208 return 1;
209 }
210
211 if (init_once) {
212 err = k5_rc_resolve(ctx, rcname, &rcache);
213 if (err) {
214 const char *msg = krb5_get_error_message(ctx, err);
215 fprintf(stderr, "%s: %s while initializing new replay cache\n",
216 prog, msg);
217 krb5_free_error_message(ctx, msg);
218 return 1;
219 }
220 }
221
222 ip = malloc(sizeof(int) * n_threads);
223 if (ip == 0 && n_threads > 0) {
224 perror("malloc");
225 exit(1);
226 }
227 for (i = 0; i < n_threads; i++)
228 ip[i] = i;
229
230 wait_for_tick ();
231 end_time = time(0) + interval;
232
233 for (i = 0; i < n_threads; i++) {
234 pthread_t new_thread;
235 int perr;
236 perr = pthread_create(&new_thread, 0, run_a_loop, &ip[i]);
237 if (perr) {
238 errno = perr;
239 perror("pthread_create");
240 exit(1);
241 }
242 }
243 while (time(0) < end_time + 1)
244 sleep(1);
245 sum = 0;
246 for (i = 0; i < n_threads; i++) {
247 sum += ip[i];
248 printf("thread %d total %5d, about %.1f per second\n", i, ip[i],
249 ((double) ip[i])/interval);
250 }
251 printf("total %lu in %d seconds, avg ~%.1f/sec, ~%.1f/sec/thread\n",
252 sum, interval,
253 ((double)sum)/interval, ((double)sum)/interval/n_threads);
254 free(ip);
255
256 if (init_once)
257 k5_rc_close(ctx, rcache);
258 krb5_free_context(ctx);
259 return 0;
260 }
261