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