rx: Don't cast returns from allocator
[openafs.git] / src / rxkad / test / stress_c.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* RX Authentication Stress test: client side code. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 #include <roken.h>
16
17 #include <afs/stds.h>
18
19 #include <afs/com_err.h>
20 #include <afs/afsutil.h>
21 #include <rx/rxkad.h>
22 #include <afs/auth.h>
23 #include <des_prototypes.h>
24 #include "stress.h"
25 #include "stress_internal.h"
26 #ifdef AFS_PTHREAD_ENV
27 #include <pthread.h>
28 #define FT_ApproxTime() (int)time(0)
29 #endif
30
31 extern int maxSkew;
32
33 static char *whoami;
34
35 static long
36 GetServer(const char *aname)
37 {
38     struct hostent *th;
39     long addr;
40
41     th = gethostbyname(aname);
42     if (!th) {
43         fprintf(stderr, "host %s not found\n", aname);
44         return errno;
45     }
46     memcpy(&addr, th->h_addr, sizeof(addr));
47     return addr;
48 }
49
50 static long
51 GetToken(long *versionP, struct ktc_encryptionKey *session,
52          int *ticketLenP, char *ticket, char *cell)
53 {
54     struct ktc_principal sname;
55     struct ktc_token ttoken;
56     long code;
57
58     strcpy(sname.cell, cell);
59     sname.instance[0] = 0;
60     strcpy(sname.name, "afs");
61     code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
62     if (code)
63         return code;
64
65     *versionP = ttoken.kvno;
66     *ticketLenP = ttoken.ticketLen;
67     memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
68     memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
69     return 0;
70 }
71
72 static long
73 GetTicket(long *versionP, struct ktc_encryptionKey *session,
74           int *ticketLenP, char *ticket, char *cell)
75 {
76     long code;
77
78     /* create random session key, using key for seed to good random */
79     des_init_random_number_generator(ktc_to_cblock(&serviceKey));
80     code = des_random_key(ktc_to_cblock(session));
81     if (code)
82         return code;
83
84     /* now create the actual ticket */
85     *ticketLenP = 0;
86     code =
87         tkt_MakeTicket(ticket, ticketLenP, &serviceKey, RXKST_CLIENT_NAME,
88                        RXKST_CLIENT_INST, cell,
89                        /*start,end */ 0, 0xffffffff, session, /*host */ 0,
90                        RXKST_SERVER_NAME, RXKST_SERVER_NAME);
91     /* parms were buffer, ticketlen, key to seal ticket with, principal name,
92      * instance and cell, start time, end time, session key to seal in ticket,
93      * inet host, server name and server instance */
94     if (code)
95         return code;
96     *versionP = serviceKeyVersion;
97     return 0;
98 }
99 struct client {
100     struct rx_connection *conn;
101     u_long sendLen;             /* parameters for call to Copious */
102     u_long recvLen;
103     u_long *fastCalls;          /* number of calls to perform */
104     u_long *slowCalls;
105     u_long *copiousCalls;
106 };
107
108 static long
109 Copious(struct client *c, char *buf, u_long buflen)
110 {
111     long code;
112     struct rx_call *call;
113     long i;
114     long inlen = c->sendLen;
115     long outlen = c->recvLen;
116     long d = 23;
117     long mysum;
118     size_t outsum;
119
120     mysum = 0;
121     for (i = 0; i < inlen; i++)
122         mysum += (d++ & 0xff);
123
124     call = rx_NewCall(c->conn);
125     code = StartRXKST_Copious(call, inlen, mysum, outlen);
126     if (code == 0) {
127         long tlen;
128         long xfer = 0;
129         long n;
130         d = 23;
131         while (xfer < inlen) {
132             tlen = inlen - xfer;
133             if (tlen > buflen)
134                 tlen = buflen;
135             for (i = 0; i < tlen; i++)
136                 buf[i] = (d++ & 0xff);
137             n = rx_Write(call, buf, tlen);
138             if (n != tlen) {
139                 if (n < 0)
140                     code = n;
141                 else
142                     code = RXKST_WRITESHORT;
143                 break;
144             }
145             xfer += tlen;
146         }
147         if (code == 0) {
148             xfer = 0;
149             mysum = 0;
150             while (xfer < outlen) {
151                 tlen = outlen - xfer;
152                 if (tlen > buflen)
153                     tlen = buflen;
154                 n = rx_Read(call, buf, tlen);
155                 if (n != tlen) {
156                     if (n < 0)
157                         code = n;
158                     else
159                         code = RXKST_READSHORT;
160                     break;
161                 }
162                 for (i = 0; i < tlen; i++)
163                     mysum += buf[i];
164                 xfer += tlen;
165             }
166         }
167     }
168     if (code == 0)
169         code = EndRXKST_Copious(call, &outsum);
170     code = rx_EndCall(call, code);
171     if (code)
172         return code;
173     if (outsum != mysum) {
174         return RXKST_BADOUTPUTSUM;
175     }
176     return 0;
177 }
178
179 static long
180 DoClient(int index, opaque rock)
181 {
182     struct client *c = (struct client *)rock;
183     long code = 0;
184     int i;
185     u_long n, inc_n;
186
187     n = 95678;
188     for (i = 0; i < c->fastCalls[index]; i++) {
189         code = RXKST_Fast(c->conn, n, &inc_n);
190         if (code)
191             return (code);
192         if (n + 1 != inc_n)
193             return RXKST_INCFAILED;
194         n++;
195     }
196
197     for (i = 0; i < c->slowCalls[index]; i++) {
198         u_long ntime;
199         u_long now;
200         code = RXKST_Slow(c->conn, 1, &ntime);
201         if (code)
202             return (code);
203         now = FT_ApproxTime();
204         if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
205             return RXKST_TIMESKEW;
206     }
207
208     if (c->copiousCalls[index] > 0) {
209         u_long buflen = 10000;
210         char *buf = osi_Alloc(buflen);
211         for (i = 0; i < c->copiousCalls[index]; i++) {
212             code = Copious(c, buf, buflen);
213             if (code)
214                 break;
215         }
216         osi_Free(buf, buflen);
217         if (code)
218             return code;
219     }
220     return 0;
221 }
222
223 struct worker {
224     struct worker *next;
225     long exitCode;              /* is PROCESSRUNNING until exit */
226     int index;
227     opaque rock;
228     long (*proc) (int, opaque);
229 };
230
231 #ifdef AFS_PTHREAD_ENV
232 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
233 static pthread_mutex_t workerLock;
234 static pthread_cond_t workerCV;
235 void
236 WorkerInit(void)
237 {
238     pthread_mutex_init(&workerLock, NULL);
239     pthread_cond_init(&workerCV, NULL);
240 }
241 #endif
242 static struct worker *workers;
243
244 static void *
245 DoWorker(void * rock)
246 {
247     struct worker *w = rock;
248     long code;
249     code = (*w->proc) (w->index, w->rock);
250 #ifdef AFS_PTHREAD_ENV
251     pthread_mutex_lock(&workerLock);
252 #endif
253     w->exitCode = code;
254 #ifdef AFS_PTHREAD_ENV
255     pthread_mutex_unlock(&workerLock);
256 #endif
257 #ifdef AFS_PTHREAD_ENV
258     pthread_cond_signal(&workerCV);
259 #else
260     LWP_NoYieldSignal(&workers);
261 #endif
262     return (void *)(intptr_t)code;
263 }
264
265 #define MAX_CTHREADS 25
266
267 static long
268 CallSimultaneously(u_int threads, opaque rock, long (*proc)(int, opaque))
269 {
270     long code;
271     int i;
272 #ifdef AFS_PTHREAD_ENV
273     pthread_once(&workerOnce, WorkerInit);
274 #endif
275
276     workers = 0;
277     for (i = 0; i < threads; i++) {
278         struct worker *w;
279 #ifdef AFS_PTHREAD_ENV
280         pthread_t pid;
281 #else
282         PROCESS pid;
283 #endif
284         assert(i < MAX_CTHREADS);
285         w = osi_Alloc(sizeof(struct worker));
286         memset(w, 0, sizeof(*w));
287         w->next = workers;
288         workers = w;
289         w->index = i;
290         w->exitCode = RXKST_PROCESSRUNNING;
291         w->rock = rock;
292         w->proc = proc;
293 #ifdef AFS_PTHREAD_ENV
294         {
295             pthread_attr_t tattr;
296
297             code = pthread_attr_init(&tattr);
298             if (code) {
299                 afs_com_err(whoami, code,
300                         "can't pthread_attr_init worker process");
301                 return code;
302             }
303
304             code =
305                 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
306             if (code) {
307                 afs_com_err(whoami, code,
308                         "can't pthread_attr_setdetachstate worker process");
309                 return code;
310             }
311
312             code = pthread_create(&pid, &tattr, DoWorker, (void *)w);
313         }
314 #else
315         code =
316             LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
317                               (opaque) w, "Worker Process", &pid);
318 #endif
319         if (code) {
320             afs_com_err(whoami, code, "can't create worker process");
321             return code;
322         }
323     }
324     code = 0;                   /* last non-zero code encountered */
325 #ifdef AFS_PTHREAD_ENV
326     pthread_mutex_lock(&workerLock);
327 #endif
328     while (workers) {
329         struct worker *w, *prevW, *nextW;
330         prevW = 0;
331         for (w = workers; w; w = nextW) {
332             nextW = w->next;
333             if (w->exitCode != RXKST_PROCESSRUNNING) {
334                 if (w->exitCode) {
335                     if (code == 0)
336                         code = w->exitCode;
337                 }
338                 if (prevW)
339                     prevW->next = w->next;
340                 else
341                     workers = w->next;
342                 osi_Free(w, sizeof(*w));
343                 continue;       /* don't bump prevW */
344             }
345             prevW = w;
346         }
347 #ifdef AFS_PTHREAD_ENV
348         if (workers)
349             pthread_cond_wait(&workerCV, &workerLock);
350 #else
351         if (workers)
352             LWP_WaitProcess(&workers);
353 #endif
354     }
355 #ifdef AFS_PTHREAD_ENV
356     pthread_mutex_unlock(&workerLock);
357 #endif
358     return code;
359 }
360
361 static void
362 DivideUpCalls(u_long calls, u_int threads, u_long threadCalls[])
363 {
364     int i;
365     for (i = 0; i < threads; i++) {
366         threadCalls[i] = calls / (threads - i);
367         calls -= threadCalls[i];
368     }
369 }
370
371 static double
372 ftime(void)
373 {
374     struct timeval tv;
375     gettimeofday(&tv, NULL);
376     return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
377 }
378
379 static long
380 RunLoadTest(struct clientParms *parms, struct rx_connection *conn)
381 {
382     long code;
383     struct client c;
384     u_long fastCalls[MAX_CTHREADS];
385     u_long slowCalls[MAX_CTHREADS];
386     u_long copiousCalls[MAX_CTHREADS];
387     double start, interval;
388
389     DivideUpCalls(parms->fastCalls, parms->threads, fastCalls);
390     DivideUpCalls(parms->slowCalls, parms->threads, slowCalls);
391     DivideUpCalls(parms->copiousCalls, parms->threads, copiousCalls);
392
393     memset(&c, 0, sizeof(c));
394     c.conn = conn;
395     c.sendLen = parms->sendLen;
396     c.recvLen = parms->recvLen;
397     c.fastCalls = fastCalls;
398     c.slowCalls = slowCalls;
399     c.copiousCalls = copiousCalls;
400
401     start = ftime();
402     code = CallSimultaneously(parms->threads, &c, DoClient);
403     if (code) {
404         afs_com_err(whoami, code, "in DoClient");
405         return code;
406     }
407     interval = ftime() - start;
408
409     if (parms->printTiming) {
410         u_long totalCalls =
411             parms->fastCalls + parms->slowCalls + parms->copiousCalls;
412         int t = (interval / totalCalls) * 1000.0 + 0.5;
413         if (totalCalls > 0) {
414             printf("For %lu calls: %d msec/call\n", totalCalls, t);
415         }
416         if (parms->copiousCalls > 0) {
417             long n = parms->sendLen + parms->recvLen;
418             double kbps;
419             int b;
420             kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
421             b = kbps + 0.5;
422 #if 0
423             I just cannot get printing of floats to work on the pmax !
424                 !!!printf("%g %d %d %d\n", (float)kbps, b);
425             printf("%g %d %d %d\n", kbps, b);
426             fprintf(stdout, "%g %d %d\n", kbps, b);
427             {
428                 char buf[100];
429                 buf[sizeof(buf) - 1] = 0;
430                 sprintf(buf, "%g %d %d\n", kbps, b);
431                 assert(buf[sizeof(buf) - 1] == 0);
432                 printf("%s", buf);
433             }
434 #endif
435             printf
436                 ("For %lu copious calls, %lu send + %lu recv = %lu bytes each: %d kbytes/sec\n",
437                  parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
438 #if 0
439             printf("%g\n", kbps);
440 #endif
441         }
442     }
443     return 0;
444 }
445
446 static long
447 RepeatLoadTest(struct clientParms *parms, struct rx_connection *conn)
448 {
449     long code;
450     long count;
451
452     if (parms->repeatInterval == 0) {
453         if (parms->repeatCount == 0)
454             parms->repeatCount = 1;
455     } else {
456         if (parms->repeatCount == 0)
457             parms->repeatCount = 0x7fffffff;
458     }
459
460     if (parms->printTiming) {
461         int types;
462         types =
463             (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
464             (parms->copiousCalls ? 1 : 0);
465         if (types > 1)
466             fprintf(stderr,
467                     "Combined timings of several types of calls may not be meaningful.\n");
468         if (types == 0)
469             /* do timings of copious calls by default */
470             parms->copiousCalls = 10;
471     }
472
473     for (count = 0; count < parms->repeatCount; count++) {
474         code = RunLoadTest(parms, conn);
475         if (code)
476             return code;
477         if (parms->repeatInterval) {
478             u_long i = parms->repeatInterval;
479             u_long now = time(0);
480             u_long next = (now + i - 1) / i * i;        /* round up to next interval */
481             while (now < next) {
482 #ifdef AFS_PTHREAD_ENV
483                 sleep(next - now);
484 #else
485                 IOMGR_Sleep(next - now);
486 #endif
487                 now = time(0);
488             }
489         }
490     }
491     return code;
492 }
493
494 /* For backward compatibility, don't try to use the CallNumber stuff unless
495  * we're compiling against the new Rx. */
496
497 #ifdef rx_GetPacketCksum
498
499 struct multiChannel {
500     struct rx_connection *conn;
501     int done;
502     long *codes;
503     int changes[RX_MAXCALLS];
504     afs_int32 callNumbers[RX_MAXCALLS];
505 };
506 #define BIG_PRIME 1257056893    /* 0x4AED2A7d */
507 static u_long sequence = 0;
508
509 static long
510 FastCall(struct rx_connection *conn)
511 {
512     long code;
513     u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
514     u_long inc_n;
515
516     code = RXKST_Fast(conn, n, &inc_n);
517     if (code)
518         return code;
519     if (inc_n != n + 1)
520         return RXKST_INCFAILED;
521     return 0;
522 }
523
524 static long
525 UniChannelCall(int index, opaque rock)
526 {
527     struct multiChannel *mc = (struct multiChannel *)rock;
528     long code;
529     afs_int32 callNumbers[RX_MAXCALLS];
530     int unchanged;
531
532     code = 0;
533     unchanged = 1;
534     while (!mc->done && unchanged) {
535         int i;
536         code = FastCall(mc->conn);
537         if (code)
538             break;
539         code = rxi_GetCallNumberVector(mc->conn, callNumbers);
540         if (code)
541             break;
542         unchanged = 0;
543         for (i = 0; i < RX_MAXCALLS; i++) {
544             if (callNumbers[i] > mc->callNumbers[i]) {
545                 mc->callNumbers[i] = callNumbers[i];
546                 mc->changes[i]--;       /* may go negative */
547             }
548             if (mc->changes[i] > 0)
549                 unchanged++;
550         }
551     }
552     mc->codes[index] = code;
553     mc->done++;
554     return code;
555 }
556
557 static long
558 MakeMultiChannelCall(struct rx_connection *conn, int each, 
559                      long expectedCode, long codes[])
560 {
561     long code;
562     int i;
563     struct multiChannel mc;
564
565     memset(&mc, 0, sizeof(mc));
566     mc.conn = conn;
567     for (i = 0; i < RX_MAXCALLS; i++) {
568         codes[i] = RXKST_PROCESSRUNNING;
569         mc.changes[i] = each;
570     }
571     mc.codes = codes;
572     code = rxi_GetCallNumberVector(conn, mc.callNumbers);
573     if (code)
574         return code;
575     mc.done = 0;
576     code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
577     if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3)));       /* strange cases */
578     else if (code != expectedCode) {
579         afs_com_err(whoami, code,
580                 "problem making multichannel call, expected '%s'",
581                 ((expectedCode == 0)
582                  ? "no error" : (char *)afs_error_message(expectedCode)));
583     }
584     return code;
585 }
586
587 static long
588 CheckCallFailure(struct rx_connection *conn, long codes[], long code,
589                  char *msg)
590 {
591     if (code == 0) {
592         fprintf(stderr, "Failed to detect %s\n", msg);
593         return RXKST_NODUPLICATECALL;
594     } else {
595         int i;
596         int okay = 1;
597         int someZero = 0;
598         for (i = 0; i < RX_MAXCALLS; i++)
599             if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
600                 okay = 0;
601         if (conn->error)
602             okay = 0;
603         if (!okay) {
604             fprintf(stderr, "%s produced these errors:\n", msg);
605             for (i = 0; i < RX_MAXCALLS; i++) {
606                 assert(codes[i] != RXKST_PROCESSRUNNING);
607                 if (codes[i] == 0) {
608                     someZero++;
609                     fprintf(stderr, "  %d no error\n", i);
610                 } else
611                     fprintf(stderr, "  %d %s\n", i, afs_error_message(codes[i]));
612             }
613             if (someZero) {
614                 char buf[100];
615                 sprintf(buf, "connection dead following %s", msg);
616                 code = FastCall(conn);
617                 if (code)
618                     afs_com_err(whoami, code, "%s", buf);
619             }
620         }
621     }
622     return 0;
623 }
624
625 #endif /* rx_GetPacketCksum */
626
627 static long
628 RunCallTest(struct clientParms *parms, long host,
629             struct rx_securityClass *sc, long si)
630 {
631     long code;
632
633 #ifndef rx_GetPacketCksum
634
635     code = RXKST_BADARGS;
636     afs_com_err(whoami, code,
637             "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
638     return code;
639
640 #else
641
642     int i, ch;
643     struct rx_connection *conn;
644     long firstCall;
645     afs_int32 callNumbers[RX_MAXCALLS];
646     long codes[RX_MAXCALLS];
647     long retCode = 0;           /* ret. if nothing fatal goes wrong */
648
649     conn =
650         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
651                          si);
652     if (!conn)
653         return RXKST_NEWCONNFAILED;
654
655     /* First check the basic behaviour of call number handling */
656
657     code = rxi_GetCallNumberVector(conn, callNumbers);
658     if (code)
659         return code;
660     for (i = 0; i < RX_MAXCALLS; i++) {
661         if (callNumbers[i] != 0) {
662             fprintf(stderr,
663                     "Connection's initial call numbers not zero. call[%d] = %d\n",
664                     i, callNumbers[i]);
665             return RXKST_BADCALLNUMBERS;
666         }
667     }
668     code = FastCall(conn);
669     if (code)
670         return code;
671     code = rxi_GetCallNumberVector(conn, callNumbers);
672     if (code)
673         return code;
674     firstCall = callNumbers[0];
675     code = FastCall(conn);
676     if (code)
677         return code;
678     code = rxi_GetCallNumberVector(conn, callNumbers);
679     if (code)
680         return code;
681     if ((callNumbers[0] != firstCall + 1)
682         && ((firstCall == 1) || (firstCall == 2))) {
683         /* The call number after the first call should be one or, more likely,
684          * two (if the call is still DALLYing).  Between first and second call,
685          * the call number should have incremented by one. */
686         fprintf(stderr,
687                 "Connection's first channel call number not one. call[%d] = %d\n",
688                 0, callNumbers[0]);
689         return RXKST_BADCALLNUMBERS;
690     }
691     for (i = 1; i < RX_MAXCALLS; i++) {
692         if (callNumbers[i] != 0) {
693             fprintf(stderr,
694                     "Connection's other channel call numbers not zero. call[%d] = %d\n",
695                     i, callNumbers[i]);
696             return RXKST_BADCALLNUMBERS;
697         }
698     }
699     code = MakeMultiChannelCall(conn, 1, 0, codes);
700     if (code)
701         return code;
702
703     /* Now try to resend a call that's already been executed by finding a
704      * non-zero call number on a channel other than zero and decrementing it by
705      * one.  This should appear to the server as a retransmitted call.  Since
706      * this is behaving as a broken client different strange behaviors may be
707      * exhibited by different servers.  If the response packet to the original
708      * call is discarded by the time the "retransmitted" call arrives (perhaps
709      * due to high server or client load) there is no way for the server to
710      * respond at all.  Further, it seems, that under some cases the connection
711      * will be kept alive indefinitely even though the server has discarded the
712      * "retransmitted" call and is making no effort to reexecute the call.  To
713      * handle these, accept either a timeout (-1) or and INCFAILED error here,
714      * also set the connenction HardDeadTime to punt after a reasonable
715      * interval. */
716
717     /* short dead time since may we expect some trouble */
718     rx_SetConnHardDeadTime(conn, 30);
719     code = rxi_GetCallNumberVector(conn, callNumbers);
720     if (code)
721         return code;
722     for (ch = 1; ch < RX_MAXCALLS; ch++)
723         if (callNumbers[ch] > 1) {
724             callNumbers[ch]--;
725             code = rxi_SetCallNumberVector(conn, callNumbers);
726             if (code)
727                 return code;
728             break;
729         }
730     if (ch >= RX_MAXCALLS)      /* didn't find any? all DALLYing? */
731         return RXKST_BADCALLNUMBERS;
732     code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
733     code = CheckCallFailure(conn, codes, code, "retransmitted call");
734     if (code && !retCode)
735         retCode = code;
736
737     /* Get a fresh connection, becasue if the above failed as it should the
738      * connection is dead. */
739     rx_DestroyConnection(conn);
740     conn =
741         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
742                          si);
743     if (!conn)
744         return RXKST_NEWCONNFAILED;
745
746     /* Similarly, but decrement call number by two which should be completely
747      * unmistakeable as a broken or malicious client. */
748
749     /* short dead time since may we expect some trouble */
750     rx_SetConnHardDeadTime(conn, 30);
751     code = MakeMultiChannelCall(conn, 2, 0, codes);
752     if (code)
753         return code;
754     code = rxi_GetCallNumberVector(conn, callNumbers);
755     if (code)
756         return code;
757     for (ch = 1; ch < RX_MAXCALLS; ch++)
758         if (callNumbers[ch] > 2) {
759             callNumbers[ch] -= 2;
760             code = rxi_SetCallNumberVector(conn, callNumbers);
761             break;
762         }
763     if (ch >= RX_MAXCALLS)      /* didn't find any? all DALLYing? */
764         return RXKST_BADCALLNUMBERS;
765     code = MakeMultiChannelCall(conn, 1, -1, codes);
766     code = CheckCallFailure(conn, codes, code, "duplicate call");
767     if (code && !retCode)
768         retCode = code;
769
770     rx_DestroyConnection(conn);
771     conn =
772         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
773                          si);
774     if (!conn)
775         return RXKST_NEWCONNFAILED;
776
777     /* Next, without waiting for the server to discard its state, we will check
778      * to see if the Challenge/Response protocol correctly informs the server
779      * of the client's callNumber state.  We do this by artificially increasing
780      * the call numbers of a new connection for all channels beyond zero,
781      * making a call on channel zero, then resetting the call number for the
782      * unused channels back to zero, then making calls on all channels. */
783
784     code = rxi_GetCallNumberVector(conn, callNumbers);
785     if (code)
786         return code;
787     for (i = 0; i < RX_MAXCALLS; i++) {
788         if (callNumbers[i] != 0)
789             return RXKST_BADCALLNUMBERS;
790         callNumbers[i] = 51;    /* an arbitrary value... */
791     }
792     code = rxi_SetCallNumberVector(conn, callNumbers);
793     if (code)
794         return code;
795     code = FastCall(conn);      /* use channel 0 */
796     if (code)
797         return code;
798     code = rxi_GetCallNumberVector(conn, callNumbers);
799     if (code)
800         return code;
801     if (callNumbers[0] != 52)
802         return RXKST_BADCALLNUMBERS;
803     for (i = 1; i < RX_MAXCALLS; i++) {
804         if (callNumbers[i] != 51)
805             return RXKST_BADCALLNUMBERS;
806         callNumbers[i] = 37;    /* back up a ways */
807     }
808     code = rxi_SetCallNumberVector(conn, callNumbers);
809     if (code)
810         return code;
811     /* now try calls on all channels... */
812     code = MakeMultiChannelCall(conn, 1, -1, codes);
813     code =
814         CheckCallFailure(conn, codes, code, "alternate channel call replay");
815     if (code && !retCode)
816         retCode = code;
817
818     rx_DestroyConnection(conn);
819     return retCode;
820
821 #endif /* rx_GetPacketCksum */
822
823 }
824
825 #ifdef rx_GetPacketCksum
826
827 static struct {
828     int op;
829     u_long epoch;               /* connection to attack */
830     u_long cid;
831     int client;                 /* TRUE => client side */
832     u_long newEpoch;            /* conn to direct challenges to */
833     u_long newCid;
834     u_long counts[RX_N_PACKET_TYPES];
835 } incomingOps;
836 #define IO_NOOP                 0
837 #define IO_COUNT                1
838 #define IO_REDIRECTCHALLENGE    2
839
840 static int
841 HandleIncoming(struct rx_packet *p, struct sockaddr_in *addr)
842 {
843     int client;                 /* packet sent by client */
844     u_char type;                /* packet type */
845
846     if (incomingOps.op == IO_NOOP)
847         return 0;
848
849     client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
850     if ((p->header.epoch != incomingOps.epoch)
851         || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
852         || (client != incomingOps.client))
853         return 0;
854     type = p->header.type;
855     if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
856         type = 0;
857     incomingOps.counts[type]++;
858
859     switch (incomingOps.op) {
860     case IO_NOOP:
861     case IO_COUNT:
862         break;
863
864     case IO_REDIRECTCHALLENGE:
865         if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
866             break;
867         p->header.epoch = incomingOps.newEpoch;
868         p->header.cid = incomingOps.newCid;
869         /* Now set up to watch for the corresponding challenge. */
870         incomingOps.epoch = incomingOps.newEpoch;
871         incomingOps.cid = incomingOps.newCid;
872         incomingOps.op = IO_COUNT;
873         break;
874
875     default:
876         fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
877         break;
878     }
879     return 0;
880 }
881
882 static struct {
883     int op;
884     u_long epoch;               /* connection to attack */
885     u_long cid;
886     int client;                 /* TRUE => client side */
887     u_long counts[RX_N_PACKET_TYPES];
888 } outgoingOps;
889 #define OO_NOOP         0
890 #define OO_COUNT        1
891 #define OO_ZEROCKSUM    2
892 #define OO_MUNGCKSUM    3
893
894 static int
895 HandleOutgoing(struct rx_packet *p, struct sockaddr_in *addr)
896 {
897     int client;                 /* packet sent by client */
898     u_char type;                /* packet type */
899
900     if (outgoingOps.op == OO_NOOP)
901         return 0;
902
903     client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
904     if ((p->header.epoch != outgoingOps.epoch)
905         || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
906         || (client != outgoingOps.client))
907         return 0;
908     type = p->header.type;
909     if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
910         type = 0;
911     outgoingOps.counts[type]++;
912
913     switch (outgoingOps.op) {
914     case OO_NOOP:
915     case OO_COUNT:
916         /* counting always happens above if not noop */
917         break;
918
919     case OO_ZEROCKSUM:
920         if (p->header.type != RX_PACKET_TYPE_DATA)
921             break;
922         if (rx_GetPacketCksum(p) == 0) {
923             /* probably, a retransmitted packet */
924             fprintf(stderr, "Packet cksum already zero\n");
925             break;
926         }
927         rx_SetPacketCksum(p, 0);
928         break;
929
930     case OO_MUNGCKSUM:{
931             u_short cksum;
932             if (p->header.type != RX_PACKET_TYPE_DATA)
933                 break;
934             cksum = rx_GetPacketCksum(p);
935             if (cksum == 0) {
936                 fprintf(stderr, "Packet cksum already zero\n");
937                 break;
938             }
939             rx_SetPacketCksum(p, cksum ^ 8);
940             break;
941         }
942     default:
943         fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
944         break;
945     }
946     return 0;
947 }
948
949 #ifdef AFS_PTHREAD_ENV
950 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
951 static pthread_mutex_t slowCallLock;
952 static pthread_cond_t slowCallCV;
953 void
954 SlowCallInit(void)
955 {
956     pthread_mutex_init(&slowCallLock, NULL);
957     pthread_cond_init(&slowCallCV, NULL);
958 }
959 #endif
960 static long slowCallCode;
961 static void *
962 SlowCall(void * rock)
963 {
964     struct rx_connection *conn = rock;
965     u_long ntime;
966     u_long now;
967     long temp_rc;
968
969 #ifdef AFS_PTHREAD_ENV
970     pthread_mutex_lock(&slowCallLock);
971 #endif
972     slowCallCode = RXKST_PROCESSRUNNING;
973 #ifdef AFS_PTHREAD_ENV
974     pthread_cond_signal(&slowCallCV);
975 #else
976     LWP_NoYieldSignal(&slowCallCode);
977 #endif
978     slowCallCode = RXKST_Slow(conn, 1, &ntime);
979     if (!slowCallCode) {
980         now = FT_ApproxTime();
981         if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
982             slowCallCode = RXKST_TIMESKEW;
983     }
984     temp_rc = slowCallCode;
985 #ifdef AFS_PTHREAD_ENV
986     pthread_cond_signal(&slowCallCV);
987     pthread_mutex_unlock(&slowCallLock);
988 #else
989     LWP_NoYieldSignal(&slowCallCode);
990 #endif
991     return (void *)(intptr_t)temp_rc;
992 }
993
994 #endif /* rx_GetPacketCksum */
995
996 static long
997 RunHijackTest(struct clientParms *parms, long host,
998               struct rx_securityClass *sc, long si)
999 {
1000
1001 #ifndef rx_GetPacketCksum
1002
1003     code = RXKST_BADARGS;
1004     afs_com_err(whoami, code,
1005             "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
1006     return code;
1007
1008 #else
1009
1010     long code;
1011     struct rx_connection *conn = 0;
1012     struct rx_connection *otherConn = 0;
1013 #ifdef AFS_PTHREAD_ENV
1014     pthread_t pid;
1015 #else
1016     PROCESS pid;
1017 #endif
1018     int nResp;                  /* otherConn responses seen */
1019     long tmp_rc;
1020
1021 #ifdef AFS_PTHREAD_ENV
1022     pthread_once(&slowCallOnce, SlowCallInit);
1023 #endif
1024     rx_justReceived = HandleIncoming;
1025     rx_almostSent = HandleOutgoing;
1026
1027     incomingOps.op = IO_NOOP;
1028     outgoingOps.op = OO_NOOP;
1029
1030 #define HIJACK_CONN(conn) \
1031     {   if (conn) rx_DestroyConnection (conn); \
1032         (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1033                                 RXKST_SERVICEID, sc, si); \
1034         if (!(conn)) return RXKST_NEWCONNFAILED; \
1035         outgoingOps.client = 1; \
1036         outgoingOps.epoch = (conn)->epoch; \
1037         outgoingOps.cid = (conn)->cid; }
1038
1039     HIJACK_CONN(conn);
1040
1041     /* First try switching from no packet cksum to sending packet cksum between
1042      * calls, and see if server complains. */
1043
1044     outgoingOps.op = OO_ZEROCKSUM;
1045     code = FastCall(conn);
1046     if (code) {
1047         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1048         return code;
1049     }
1050     /* The server thinks we're an old style client.  Now start sending cksums.
1051      * Server shouldn't care. */
1052     outgoingOps.op = OO_NOOP;
1053     code = FastCall(conn);
1054     if (code) {
1055         afs_com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1056         return code;
1057     }
1058     /* The server now thinks we're a new style client, we can't go back now. */
1059     outgoingOps.op = OO_ZEROCKSUM;
1060     code = FastCall(conn);
1061     if (code == 0)
1062         code = RXKST_NOBADCKSUM;
1063     if (code != RXKADSEALEDINCON) {
1064         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1065         return code;
1066     } else if (!conn->error) {
1067         code = RXKST_NOCONNERROR;
1068         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1069         return code;
1070     } else
1071         code = 0;
1072
1073     HIJACK_CONN(conn);
1074
1075     /* Now try modifying packet cksum to see if server complains. */
1076
1077     outgoingOps.op = OO_MUNGCKSUM;
1078     code = FastCall(conn);
1079     if (code == 0)
1080         code = RXKST_NOBADCKSUM;
1081     if (code != RXKADSEALEDINCON) {
1082         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1083         return code;
1084     } else if (!conn->error) {
1085         code = RXKST_NOCONNERROR;
1086         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1087         return code;
1088     } else
1089         code = 0;
1090
1091     /* Now make two connection and direct the first challenge on one connection
1092      * to the other connection to see if it generates a response.  The
1093      * retransmitted challenge should allow the call on the first connection to
1094      * complete correctly.  Part one is to attack a new connection, then attack
1095      * it after it has made a call.  Part three, just for comparison, attacks a
1096      * otherConn while it is making a slow call (and thus has an active call).
1097      * Against this attack we have no defense so we expect a challenge in this
1098      * case, which the server will discard. */
1099
1100 #define RedirectChallenge(conn,otherConn)       \
1101     (incomingOps.epoch = (conn)->epoch,         \
1102      incomingOps.cid = (conn)->cid,             \
1103      incomingOps.client = 1,                    \
1104      incomingOps.newEpoch = (otherConn)->epoch, \
1105      incomingOps.newCid = (otherConn)->cid,     \
1106      incomingOps.op = IO_REDIRECTCHALLENGE,     \
1107      outgoingOps.epoch = (otherConn)->epoch,    \
1108      outgoingOps.cid = (otherConn)->cid,        \
1109      outgoingOps.client = 1,                    \
1110      outgoingOps.op = OO_COUNT,                 \
1111      outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1112
1113     HIJACK_CONN(conn);
1114     HIJACK_CONN(otherConn)
1115         RedirectChallenge(conn, otherConn);
1116
1117     code = FastCall(conn);
1118     if (code)
1119         return code;
1120     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1121     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1122       oracle:
1123         code = RXKST_CHALLENGEORACLE;
1124         afs_com_err(whoami, code, "misdirecting challenge");
1125         return code;
1126     }
1127     code = FastCall(otherConn); /* generate some activity here */
1128     if (code)
1129         return code;
1130     nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1131     assert(nResp >= 1);
1132     code = FastCall(conn);
1133     if (code)
1134         return code;
1135     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1136         goto oracle;
1137
1138     HIJACK_CONN(conn);
1139     RedirectChallenge(conn, otherConn);
1140     /* otherConn was authenticated during part one */
1141     code = FastCall(conn);
1142     if (code)
1143         return code;
1144     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1145     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1146         goto oracle;
1147
1148     HIJACK_CONN(conn);
1149     RedirectChallenge(conn, otherConn);
1150     /* otherConn is still authenticated */
1151     slowCallCode = RXKST_PROCESSCREATED;
1152 #ifdef AFS_PTHREAD_ENV
1153     {
1154         pthread_attr_t tattr;
1155
1156         code = pthread_attr_init(&tattr);
1157         if (code) {
1158             afs_com_err(whoami, code,
1159                     "can't pthread_attr_init slow call process");
1160             return code;
1161         }
1162
1163         code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1164         if (code) {
1165             afs_com_err(whoami, code,
1166                     "can't pthread_attr_setdetachstate slow call process");
1167             return code;
1168         }
1169
1170         code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1171     }
1172 #else
1173     code =
1174         LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1175                           (opaque) otherConn, "Slow Call Process", &pid);
1176 #endif
1177     if (code) {
1178         afs_com_err(whoami, code, "can't create slow call process");
1179         return code;
1180     }
1181 #ifdef AFS_PTHREAD_ENV
1182     pthread_mutex_lock(&slowCallLock);
1183     while (slowCallCode == RXKST_PROCESSCREATED)
1184         pthread_cond_wait(&slowCallCV, &slowCallLock);
1185 #else
1186     while (slowCallCode == RXKST_PROCESSCREATED)
1187         LWP_WaitProcess(&slowCallCode); /* wait for process start */
1188 #endif
1189     if (slowCallCode != RXKST_PROCESSRUNNING) {
1190         tmp_rc = slowCallCode;
1191 #ifdef AFS_PTHREAD_ENV
1192         pthread_mutex_unlock(&slowCallLock);
1193 #endif
1194         return tmp_rc;          /* make sure didn't fail immediately */
1195     }
1196     assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1197     code = FastCall(conn);
1198     if (code)
1199         return code;
1200     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1201 #ifdef AFS_PTHREAD_ENV
1202     while (slowCallCode == RXKST_PROCESSRUNNING)
1203         pthread_cond_wait(&slowCallCV, &slowCallLock);
1204     pthread_mutex_unlock(&slowCallLock);
1205 #else
1206     while (slowCallCode == RXKST_PROCESSRUNNING)
1207         LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1208 #endif
1209     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1210         goto oracle;
1211
1212     rx_justReceived = 0;
1213     rx_almostSent = 0;
1214     rx_DestroyConnection(otherConn);
1215     rx_DestroyConnection(conn);
1216     return code;
1217
1218 #endif /* rx_GetPacketCksum */
1219
1220 }
1221
1222 long
1223 rxkst_StartClient(struct clientParms *parms)
1224 {
1225     long code;
1226     long host;
1227     long scIndex;
1228     struct rx_securityClass *sc;
1229
1230     whoami = parms->whoami;     /* set this global variable */
1231
1232     host = GetServer(parms->server);
1233
1234     if (parms->authentication >= 0) {
1235         long kvno = 0;
1236         char ticket[MAXKTCTICKETLEN];
1237         int ticketLen;
1238         struct ktc_encryptionKey Ksession;
1239
1240         if (parms->useTokens)
1241             code =
1242                 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1243         else
1244             code =
1245                 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1246         if (code)
1247             return code;
1248
1249         /* next, we have ticket, kvno and session key, authenticate the conn */
1250         sc = (struct rx_securityClass *)
1251             rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1252                                           kvno, ticketLen, ticket);
1253         assert(sc);
1254         scIndex = RX_SECIDX_KAD;
1255     } else {
1256         /* unauthenticated connection */
1257         sc = rxnull_NewClientSecurityObject();
1258         assert(sc);
1259         scIndex = RX_SECIDX_NULL;
1260     }
1261
1262     code = 0;
1263     if (!code && parms->callTest) {
1264         code = RunCallTest(parms, host, sc, scIndex);
1265     }
1266     if (!code && parms->hijackTest) {
1267         code = RunHijackTest(parms, host, sc, scIndex);
1268     }
1269     if (!code
1270         && (parms->printTiming || parms->fastCalls || parms->slowCalls
1271             || parms->copiousCalls)) {
1272         struct rx_connection *conn;
1273         conn =
1274             rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1275                              sc, scIndex);
1276         if (conn) {
1277             code = RepeatLoadTest(parms, conn);
1278             rx_DestroyConnection(conn);
1279         } else
1280             code = RXKST_NEWCONNFAILED;
1281     }
1282     if (!code && parms->stopServer) {
1283         struct rx_connection *conn;
1284         conn =
1285             rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1286                              sc, scIndex);
1287         if (conn) {
1288             code = RXKST_Kill(conn);
1289             if (code) {
1290                 afs_com_err(whoami, code, "trying to stop server");
1291             }
1292             rx_DestroyConnection(conn);
1293         } else
1294             code = RXKST_NEWCONNFAILED;
1295     }
1296
1297     if (parms->printStats) {
1298         rx_PrintStats(stdout);
1299 #if 0
1300         /* use rxdebug style iteration here */
1301         rx_PrintPeerStats(stdout, rx_PeerOf(conn));
1302 #endif
1303     }
1304
1305     rxs_Release(sc);
1306     rx_Finalize();
1307     if (code) {
1308         afs_com_err(parms->whoami, code, "test fails");
1309         exit(13);
1310     } else {
1311         printf("Test Okay\n");
1312         if (!parms->noExit)
1313             exit(0);
1314     }
1315     return 0;
1316 }