Remove dead code
[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             printf
423                 ("For %lu copious calls, %lu send + %lu recv = %lu bytes each: %d kbytes/sec\n",
424                  parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
425         }
426     }
427     return 0;
428 }
429
430 static long
431 RepeatLoadTest(struct clientParms *parms, struct rx_connection *conn)
432 {
433     long code;
434     long count;
435
436     if (parms->repeatInterval == 0) {
437         if (parms->repeatCount == 0)
438             parms->repeatCount = 1;
439     } else {
440         if (parms->repeatCount == 0)
441             parms->repeatCount = 0x7fffffff;
442     }
443
444     if (parms->printTiming) {
445         int types;
446         types =
447             (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
448             (parms->copiousCalls ? 1 : 0);
449         if (types > 1)
450             fprintf(stderr,
451                     "Combined timings of several types of calls may not be meaningful.\n");
452         if (types == 0)
453             /* do timings of copious calls by default */
454             parms->copiousCalls = 10;
455     }
456
457     for (count = 0; count < parms->repeatCount; count++) {
458         code = RunLoadTest(parms, conn);
459         if (code)
460             return code;
461         if (parms->repeatInterval) {
462             u_long i = parms->repeatInterval;
463             u_long now = time(0);
464             u_long next = (now + i - 1) / i * i;        /* round up to next interval */
465             while (now < next) {
466 #ifdef AFS_PTHREAD_ENV
467                 sleep(next - now);
468 #else
469                 IOMGR_Sleep(next - now);
470 #endif
471                 now = time(0);
472             }
473         }
474     }
475     return code;
476 }
477
478 /* For backward compatibility, don't try to use the CallNumber stuff unless
479  * we're compiling against the new Rx. */
480
481 #ifdef rx_GetPacketCksum
482
483 struct multiChannel {
484     struct rx_connection *conn;
485     int done;
486     long *codes;
487     int changes[RX_MAXCALLS];
488     afs_int32 callNumbers[RX_MAXCALLS];
489 };
490 #define BIG_PRIME 1257056893    /* 0x4AED2A7d */
491 static u_long sequence = 0;
492
493 static long
494 FastCall(struct rx_connection *conn)
495 {
496     long code;
497     u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
498     u_long inc_n;
499
500     code = RXKST_Fast(conn, n, &inc_n);
501     if (code)
502         return code;
503     if (inc_n != n + 1)
504         return RXKST_INCFAILED;
505     return 0;
506 }
507
508 static long
509 UniChannelCall(int index, opaque rock)
510 {
511     struct multiChannel *mc = (struct multiChannel *)rock;
512     long code;
513     afs_int32 callNumbers[RX_MAXCALLS];
514     int unchanged;
515
516     code = 0;
517     unchanged = 1;
518     while (!mc->done && unchanged) {
519         int i;
520         code = FastCall(mc->conn);
521         if (code)
522             break;
523         code = rxi_GetCallNumberVector(mc->conn, callNumbers);
524         if (code)
525             break;
526         unchanged = 0;
527         for (i = 0; i < RX_MAXCALLS; i++) {
528             if (callNumbers[i] > mc->callNumbers[i]) {
529                 mc->callNumbers[i] = callNumbers[i];
530                 mc->changes[i]--;       /* may go negative */
531             }
532             if (mc->changes[i] > 0)
533                 unchanged++;
534         }
535     }
536     mc->codes[index] = code;
537     mc->done++;
538     return code;
539 }
540
541 static long
542 MakeMultiChannelCall(struct rx_connection *conn, int each, 
543                      long expectedCode, long codes[])
544 {
545     long code;
546     int i;
547     struct multiChannel mc;
548
549     memset(&mc, 0, sizeof(mc));
550     mc.conn = conn;
551     for (i = 0; i < RX_MAXCALLS; i++) {
552         codes[i] = RXKST_PROCESSRUNNING;
553         mc.changes[i] = each;
554     }
555     mc.codes = codes;
556     code = rxi_GetCallNumberVector(conn, mc.callNumbers);
557     if (code)
558         return code;
559     mc.done = 0;
560     code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
561     if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3)));       /* strange cases */
562     else if (code != expectedCode) {
563         afs_com_err(whoami, code,
564                 "problem making multichannel call, expected '%s'",
565                 ((expectedCode == 0)
566                  ? "no error" : (char *)afs_error_message(expectedCode)));
567     }
568     return code;
569 }
570
571 static long
572 CheckCallFailure(struct rx_connection *conn, long codes[], long code,
573                  char *msg)
574 {
575     if (code == 0) {
576         fprintf(stderr, "Failed to detect %s\n", msg);
577         return RXKST_NODUPLICATECALL;
578     } else {
579         int i;
580         int okay = 1;
581         int someZero = 0;
582         for (i = 0; i < RX_MAXCALLS; i++)
583             if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
584                 okay = 0;
585         if (conn->error)
586             okay = 0;
587         if (!okay) {
588             fprintf(stderr, "%s produced these errors:\n", msg);
589             for (i = 0; i < RX_MAXCALLS; i++) {
590                 assert(codes[i] != RXKST_PROCESSRUNNING);
591                 if (codes[i] == 0) {
592                     someZero++;
593                     fprintf(stderr, "  %d no error\n", i);
594                 } else
595                     fprintf(stderr, "  %d %s\n", i, afs_error_message(codes[i]));
596             }
597             if (someZero) {
598                 char buf[100];
599                 sprintf(buf, "connection dead following %s", msg);
600                 code = FastCall(conn);
601                 if (code)
602                     afs_com_err(whoami, code, "%s", buf);
603             }
604         }
605     }
606     return 0;
607 }
608
609 #endif /* rx_GetPacketCksum */
610
611 static long
612 RunCallTest(struct clientParms *parms, long host,
613             struct rx_securityClass *sc, long si)
614 {
615     long code;
616
617 #ifndef rx_GetPacketCksum
618
619     code = RXKST_BADARGS;
620     afs_com_err(whoami, code,
621             "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
622     return code;
623
624 #else
625
626     int i, ch;
627     struct rx_connection *conn;
628     long firstCall;
629     afs_int32 callNumbers[RX_MAXCALLS];
630     long codes[RX_MAXCALLS];
631     long retCode = 0;           /* ret. if nothing fatal goes wrong */
632
633     conn =
634         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
635                          si);
636     if (!conn)
637         return RXKST_NEWCONNFAILED;
638
639     /* First check the basic behaviour of call number handling */
640
641     code = rxi_GetCallNumberVector(conn, callNumbers);
642     if (code)
643         return code;
644     for (i = 0; i < RX_MAXCALLS; i++) {
645         if (callNumbers[i] != 0) {
646             fprintf(stderr,
647                     "Connection's initial call numbers not zero. call[%d] = %d\n",
648                     i, callNumbers[i]);
649             return RXKST_BADCALLNUMBERS;
650         }
651     }
652     code = FastCall(conn);
653     if (code)
654         return code;
655     code = rxi_GetCallNumberVector(conn, callNumbers);
656     if (code)
657         return code;
658     firstCall = callNumbers[0];
659     code = FastCall(conn);
660     if (code)
661         return code;
662     code = rxi_GetCallNumberVector(conn, callNumbers);
663     if (code)
664         return code;
665     if ((callNumbers[0] != firstCall + 1)
666         && ((firstCall == 1) || (firstCall == 2))) {
667         /* The call number after the first call should be one or, more likely,
668          * two (if the call is still DALLYing).  Between first and second call,
669          * the call number should have incremented by one. */
670         fprintf(stderr,
671                 "Connection's first channel call number not one. call[%d] = %d\n",
672                 0, callNumbers[0]);
673         return RXKST_BADCALLNUMBERS;
674     }
675     for (i = 1; i < RX_MAXCALLS; i++) {
676         if (callNumbers[i] != 0) {
677             fprintf(stderr,
678                     "Connection's other channel call numbers not zero. call[%d] = %d\n",
679                     i, callNumbers[i]);
680             return RXKST_BADCALLNUMBERS;
681         }
682     }
683     code = MakeMultiChannelCall(conn, 1, 0, codes);
684     if (code)
685         return code;
686
687     /* Now try to resend a call that's already been executed by finding a
688      * non-zero call number on a channel other than zero and decrementing it by
689      * one.  This should appear to the server as a retransmitted call.  Since
690      * this is behaving as a broken client different strange behaviors may be
691      * exhibited by different servers.  If the response packet to the original
692      * call is discarded by the time the "retransmitted" call arrives (perhaps
693      * due to high server or client load) there is no way for the server to
694      * respond at all.  Further, it seems, that under some cases the connection
695      * will be kept alive indefinitely even though the server has discarded the
696      * "retransmitted" call and is making no effort to reexecute the call.  To
697      * handle these, accept either a timeout (-1) or and INCFAILED error here,
698      * also set the connenction HardDeadTime to punt after a reasonable
699      * interval. */
700
701     /* short dead time since may we expect some trouble */
702     rx_SetConnHardDeadTime(conn, 30);
703     code = rxi_GetCallNumberVector(conn, callNumbers);
704     if (code)
705         return code;
706     for (ch = 1; ch < RX_MAXCALLS; ch++)
707         if (callNumbers[ch] > 1) {
708             callNumbers[ch]--;
709             code = rxi_SetCallNumberVector(conn, callNumbers);
710             if (code)
711                 return code;
712             break;
713         }
714     if (ch >= RX_MAXCALLS)      /* didn't find any? all DALLYing? */
715         return RXKST_BADCALLNUMBERS;
716     code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
717     code = CheckCallFailure(conn, codes, code, "retransmitted call");
718     if (code && !retCode)
719         retCode = code;
720
721     /* Get a fresh connection, becasue if the above failed as it should the
722      * connection is dead. */
723     rx_DestroyConnection(conn);
724     conn =
725         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
726                          si);
727     if (!conn)
728         return RXKST_NEWCONNFAILED;
729
730     /* Similarly, but decrement call number by two which should be completely
731      * unmistakeable as a broken or malicious client. */
732
733     /* short dead time since may we expect some trouble */
734     rx_SetConnHardDeadTime(conn, 30);
735     code = MakeMultiChannelCall(conn, 2, 0, codes);
736     if (code)
737         return code;
738     code = rxi_GetCallNumberVector(conn, callNumbers);
739     if (code)
740         return code;
741     for (ch = 1; ch < RX_MAXCALLS; ch++)
742         if (callNumbers[ch] > 2) {
743             callNumbers[ch] -= 2;
744             code = rxi_SetCallNumberVector(conn, callNumbers);
745             break;
746         }
747     if (ch >= RX_MAXCALLS)      /* didn't find any? all DALLYing? */
748         return RXKST_BADCALLNUMBERS;
749     code = MakeMultiChannelCall(conn, 1, -1, codes);
750     code = CheckCallFailure(conn, codes, code, "duplicate call");
751     if (code && !retCode)
752         retCode = code;
753
754     rx_DestroyConnection(conn);
755     conn =
756         rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
757                          si);
758     if (!conn)
759         return RXKST_NEWCONNFAILED;
760
761     /* Next, without waiting for the server to discard its state, we will check
762      * to see if the Challenge/Response protocol correctly informs the server
763      * of the client's callNumber state.  We do this by artificially increasing
764      * the call numbers of a new connection for all channels beyond zero,
765      * making a call on channel zero, then resetting the call number for the
766      * unused channels back to zero, then making calls on all channels. */
767
768     code = rxi_GetCallNumberVector(conn, callNumbers);
769     if (code)
770         return code;
771     for (i = 0; i < RX_MAXCALLS; i++) {
772         if (callNumbers[i] != 0)
773             return RXKST_BADCALLNUMBERS;
774         callNumbers[i] = 51;    /* an arbitrary value... */
775     }
776     code = rxi_SetCallNumberVector(conn, callNumbers);
777     if (code)
778         return code;
779     code = FastCall(conn);      /* use channel 0 */
780     if (code)
781         return code;
782     code = rxi_GetCallNumberVector(conn, callNumbers);
783     if (code)
784         return code;
785     if (callNumbers[0] != 52)
786         return RXKST_BADCALLNUMBERS;
787     for (i = 1; i < RX_MAXCALLS; i++) {
788         if (callNumbers[i] != 51)
789             return RXKST_BADCALLNUMBERS;
790         callNumbers[i] = 37;    /* back up a ways */
791     }
792     code = rxi_SetCallNumberVector(conn, callNumbers);
793     if (code)
794         return code;
795     /* now try calls on all channels... */
796     code = MakeMultiChannelCall(conn, 1, -1, codes);
797     code =
798         CheckCallFailure(conn, codes, code, "alternate channel call replay");
799     if (code && !retCode)
800         retCode = code;
801
802     rx_DestroyConnection(conn);
803     return retCode;
804
805 #endif /* rx_GetPacketCksum */
806
807 }
808
809 #ifdef rx_GetPacketCksum
810
811 static struct {
812     int op;
813     u_long epoch;               /* connection to attack */
814     u_long cid;
815     int client;                 /* TRUE => client side */
816     u_long newEpoch;            /* conn to direct challenges to */
817     u_long newCid;
818     u_long counts[RX_N_PACKET_TYPES];
819 } incomingOps;
820 #define IO_NOOP                 0
821 #define IO_COUNT                1
822 #define IO_REDIRECTCHALLENGE    2
823
824 static int
825 HandleIncoming(struct rx_packet *p, struct sockaddr_in *addr)
826 {
827     int client;                 /* packet sent by client */
828     u_char type;                /* packet type */
829
830     if (incomingOps.op == IO_NOOP)
831         return 0;
832
833     client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
834     if ((p->header.epoch != incomingOps.epoch)
835         || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
836         || (client != incomingOps.client))
837         return 0;
838     type = p->header.type;
839     if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
840         type = 0;
841     incomingOps.counts[type]++;
842
843     switch (incomingOps.op) {
844     case IO_NOOP:
845     case IO_COUNT:
846         break;
847
848     case IO_REDIRECTCHALLENGE:
849         if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
850             break;
851         p->header.epoch = incomingOps.newEpoch;
852         p->header.cid = incomingOps.newCid;
853         /* Now set up to watch for the corresponding challenge. */
854         incomingOps.epoch = incomingOps.newEpoch;
855         incomingOps.cid = incomingOps.newCid;
856         incomingOps.op = IO_COUNT;
857         break;
858
859     default:
860         fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
861         break;
862     }
863     return 0;
864 }
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 counts[RX_N_PACKET_TYPES];
872 } outgoingOps;
873 #define OO_NOOP         0
874 #define OO_COUNT        1
875 #define OO_ZEROCKSUM    2
876 #define OO_MUNGCKSUM    3
877
878 static int
879 HandleOutgoing(struct rx_packet *p, struct sockaddr_in *addr)
880 {
881     int client;                 /* packet sent by client */
882     u_char type;                /* packet type */
883
884     if (outgoingOps.op == OO_NOOP)
885         return 0;
886
887     client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
888     if ((p->header.epoch != outgoingOps.epoch)
889         || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
890         || (client != outgoingOps.client))
891         return 0;
892     type = p->header.type;
893     if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
894         type = 0;
895     outgoingOps.counts[type]++;
896
897     switch (outgoingOps.op) {
898     case OO_NOOP:
899     case OO_COUNT:
900         /* counting always happens above if not noop */
901         break;
902
903     case OO_ZEROCKSUM:
904         if (p->header.type != RX_PACKET_TYPE_DATA)
905             break;
906         if (rx_GetPacketCksum(p) == 0) {
907             /* probably, a retransmitted packet */
908             fprintf(stderr, "Packet cksum already zero\n");
909             break;
910         }
911         rx_SetPacketCksum(p, 0);
912         break;
913
914     case OO_MUNGCKSUM:{
915             u_short cksum;
916             if (p->header.type != RX_PACKET_TYPE_DATA)
917                 break;
918             cksum = rx_GetPacketCksum(p);
919             if (cksum == 0) {
920                 fprintf(stderr, "Packet cksum already zero\n");
921                 break;
922             }
923             rx_SetPacketCksum(p, cksum ^ 8);
924             break;
925         }
926     default:
927         fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
928         break;
929     }
930     return 0;
931 }
932
933 #ifdef AFS_PTHREAD_ENV
934 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
935 static pthread_mutex_t slowCallLock;
936 static pthread_cond_t slowCallCV;
937 void
938 SlowCallInit(void)
939 {
940     pthread_mutex_init(&slowCallLock, NULL);
941     pthread_cond_init(&slowCallCV, NULL);
942 }
943 #endif
944 static long slowCallCode;
945 static void *
946 SlowCall(void * rock)
947 {
948     struct rx_connection *conn = rock;
949     u_long ntime;
950     u_long now;
951     long temp_rc;
952
953 #ifdef AFS_PTHREAD_ENV
954     pthread_mutex_lock(&slowCallLock);
955 #endif
956     slowCallCode = RXKST_PROCESSRUNNING;
957 #ifdef AFS_PTHREAD_ENV
958     pthread_cond_signal(&slowCallCV);
959 #else
960     LWP_NoYieldSignal(&slowCallCode);
961 #endif
962     slowCallCode = RXKST_Slow(conn, 1, &ntime);
963     if (!slowCallCode) {
964         now = FT_ApproxTime();
965         if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
966             slowCallCode = RXKST_TIMESKEW;
967     }
968     temp_rc = slowCallCode;
969 #ifdef AFS_PTHREAD_ENV
970     pthread_cond_signal(&slowCallCV);
971     pthread_mutex_unlock(&slowCallLock);
972 #else
973     LWP_NoYieldSignal(&slowCallCode);
974 #endif
975     return (void *)(intptr_t)temp_rc;
976 }
977
978 #endif /* rx_GetPacketCksum */
979
980 static long
981 RunHijackTest(struct clientParms *parms, long host,
982               struct rx_securityClass *sc, long si)
983 {
984
985 #ifndef rx_GetPacketCksum
986
987     code = RXKST_BADARGS;
988     afs_com_err(whoami, code,
989             "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
990     return code;
991
992 #else
993
994     long code;
995     struct rx_connection *conn = 0;
996     struct rx_connection *otherConn = 0;
997 #ifdef AFS_PTHREAD_ENV
998     pthread_t pid;
999 #else
1000     PROCESS pid;
1001 #endif
1002     int nResp;                  /* otherConn responses seen */
1003     long tmp_rc;
1004
1005 #ifdef AFS_PTHREAD_ENV
1006     pthread_once(&slowCallOnce, SlowCallInit);
1007 #endif
1008     rx_justReceived = HandleIncoming;
1009     rx_almostSent = HandleOutgoing;
1010
1011     incomingOps.op = IO_NOOP;
1012     outgoingOps.op = OO_NOOP;
1013
1014 #define HIJACK_CONN(conn) \
1015     {   if (conn) rx_DestroyConnection (conn); \
1016         (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1017                                 RXKST_SERVICEID, sc, si); \
1018         if (!(conn)) return RXKST_NEWCONNFAILED; \
1019         outgoingOps.client = 1; \
1020         outgoingOps.epoch = (conn)->epoch; \
1021         outgoingOps.cid = (conn)->cid; }
1022
1023     HIJACK_CONN(conn);
1024
1025     /* First try switching from no packet cksum to sending packet cksum between
1026      * calls, and see if server complains. */
1027
1028     outgoingOps.op = OO_ZEROCKSUM;
1029     code = FastCall(conn);
1030     if (code) {
1031         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1032         return code;
1033     }
1034     /* The server thinks we're an old style client.  Now start sending cksums.
1035      * Server shouldn't care. */
1036     outgoingOps.op = OO_NOOP;
1037     code = FastCall(conn);
1038     if (code) {
1039         afs_com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1040         return code;
1041     }
1042     /* The server now thinks we're a new style client, we can't go back now. */
1043     outgoingOps.op = OO_ZEROCKSUM;
1044     code = FastCall(conn);
1045     if (code == 0)
1046         code = RXKST_NOBADCKSUM;
1047     if (code != RXKADSEALEDINCON) {
1048         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1049         return code;
1050     } else if (!conn->error) {
1051         code = RXKST_NOCONNERROR;
1052         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1053         return code;
1054     } else
1055         code = 0;
1056
1057     HIJACK_CONN(conn);
1058
1059     /* Now try modifying packet cksum to see if server complains. */
1060
1061     outgoingOps.op = OO_MUNGCKSUM;
1062     code = FastCall(conn);
1063     if (code == 0)
1064         code = RXKST_NOBADCKSUM;
1065     if (code != RXKADSEALEDINCON) {
1066         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1067         return code;
1068     } else if (!conn->error) {
1069         code = RXKST_NOCONNERROR;
1070         afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1071         return code;
1072     } else
1073         code = 0;
1074
1075     /* Now make two connection and direct the first challenge on one connection
1076      * to the other connection to see if it generates a response.  The
1077      * retransmitted challenge should allow the call on the first connection to
1078      * complete correctly.  Part one is to attack a new connection, then attack
1079      * it after it has made a call.  Part three, just for comparison, attacks a
1080      * otherConn while it is making a slow call (and thus has an active call).
1081      * Against this attack we have no defense so we expect a challenge in this
1082      * case, which the server will discard. */
1083
1084 #define RedirectChallenge(conn,otherConn)       \
1085     (incomingOps.epoch = (conn)->epoch,         \
1086      incomingOps.cid = (conn)->cid,             \
1087      incomingOps.client = 1,                    \
1088      incomingOps.newEpoch = (otherConn)->epoch, \
1089      incomingOps.newCid = (otherConn)->cid,     \
1090      incomingOps.op = IO_REDIRECTCHALLENGE,     \
1091      outgoingOps.epoch = (otherConn)->epoch,    \
1092      outgoingOps.cid = (otherConn)->cid,        \
1093      outgoingOps.client = 1,                    \
1094      outgoingOps.op = OO_COUNT,                 \
1095      outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1096
1097     HIJACK_CONN(conn);
1098     HIJACK_CONN(otherConn)
1099         RedirectChallenge(conn, otherConn);
1100
1101     code = FastCall(conn);
1102     if (code)
1103         return code;
1104     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1105     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1106       oracle:
1107         code = RXKST_CHALLENGEORACLE;
1108         afs_com_err(whoami, code, "misdirecting challenge");
1109         return code;
1110     }
1111     code = FastCall(otherConn); /* generate some activity here */
1112     if (code)
1113         return code;
1114     nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1115     assert(nResp >= 1);
1116     code = FastCall(conn);
1117     if (code)
1118         return code;
1119     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1120         goto oracle;
1121
1122     HIJACK_CONN(conn);
1123     RedirectChallenge(conn, otherConn);
1124     /* otherConn was authenticated during part one */
1125     code = FastCall(conn);
1126     if (code)
1127         return code;
1128     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1129     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1130         goto oracle;
1131
1132     HIJACK_CONN(conn);
1133     RedirectChallenge(conn, otherConn);
1134     /* otherConn is still authenticated */
1135     slowCallCode = RXKST_PROCESSCREATED;
1136 #ifdef AFS_PTHREAD_ENV
1137     {
1138         pthread_attr_t tattr;
1139
1140         code = pthread_attr_init(&tattr);
1141         if (code) {
1142             afs_com_err(whoami, code,
1143                     "can't pthread_attr_init slow call process");
1144             return code;
1145         }
1146
1147         code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1148         if (code) {
1149             afs_com_err(whoami, code,
1150                     "can't pthread_attr_setdetachstate slow call process");
1151             return code;
1152         }
1153
1154         code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1155     }
1156 #else
1157     code =
1158         LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1159                           (opaque) otherConn, "Slow Call Process", &pid);
1160 #endif
1161     if (code) {
1162         afs_com_err(whoami, code, "can't create slow call process");
1163         return code;
1164     }
1165 #ifdef AFS_PTHREAD_ENV
1166     pthread_mutex_lock(&slowCallLock);
1167     while (slowCallCode == RXKST_PROCESSCREATED)
1168         pthread_cond_wait(&slowCallCV, &slowCallLock);
1169 #else
1170     while (slowCallCode == RXKST_PROCESSCREATED)
1171         LWP_WaitProcess(&slowCallCode); /* wait for process start */
1172 #endif
1173     if (slowCallCode != RXKST_PROCESSRUNNING) {
1174         tmp_rc = slowCallCode;
1175 #ifdef AFS_PTHREAD_ENV
1176         pthread_mutex_unlock(&slowCallLock);
1177 #endif
1178         return tmp_rc;          /* make sure didn't fail immediately */
1179     }
1180     assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1181     code = FastCall(conn);
1182     if (code)
1183         return code;
1184     assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1185 #ifdef AFS_PTHREAD_ENV
1186     while (slowCallCode == RXKST_PROCESSRUNNING)
1187         pthread_cond_wait(&slowCallCV, &slowCallLock);
1188     pthread_mutex_unlock(&slowCallLock);
1189 #else
1190     while (slowCallCode == RXKST_PROCESSRUNNING)
1191         LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1192 #endif
1193     if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1194         goto oracle;
1195
1196     rx_justReceived = 0;
1197     rx_almostSent = 0;
1198     rx_DestroyConnection(otherConn);
1199     rx_DestroyConnection(conn);
1200     return code;
1201
1202 #endif /* rx_GetPacketCksum */
1203
1204 }
1205
1206 long
1207 rxkst_StartClient(struct clientParms *parms)
1208 {
1209     long code;
1210     long host;
1211     long scIndex;
1212     struct rx_securityClass *sc;
1213
1214     whoami = parms->whoami;     /* set this global variable */
1215
1216     host = GetServer(parms->server);
1217
1218     if (parms->authentication >= 0) {
1219         long kvno = 0;
1220         char ticket[MAXKTCTICKETLEN];
1221         int ticketLen;
1222         struct ktc_encryptionKey Ksession;
1223
1224         if (parms->useTokens)
1225             code =
1226                 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1227         else
1228             code =
1229                 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1230         if (code)
1231             return code;
1232
1233         /* next, we have ticket, kvno and session key, authenticate the conn */
1234         sc = (struct rx_securityClass *)
1235             rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1236                                           kvno, ticketLen, ticket);
1237         assert(sc);
1238         scIndex = RX_SECIDX_KAD;
1239     } else {
1240         /* unauthenticated connection */
1241         sc = rxnull_NewClientSecurityObject();
1242         assert(sc);
1243         scIndex = RX_SECIDX_NULL;
1244     }
1245
1246     code = 0;
1247     if (!code && parms->callTest) {
1248         code = RunCallTest(parms, host, sc, scIndex);
1249     }
1250     if (!code && parms->hijackTest) {
1251         code = RunHijackTest(parms, host, sc, scIndex);
1252     }
1253     if (!code
1254         && (parms->printTiming || parms->fastCalls || parms->slowCalls
1255             || parms->copiousCalls)) {
1256         struct rx_connection *conn;
1257         conn =
1258             rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1259                              sc, scIndex);
1260         if (conn) {
1261             code = RepeatLoadTest(parms, conn);
1262             rx_DestroyConnection(conn);
1263         } else
1264             code = RXKST_NEWCONNFAILED;
1265     }
1266     if (!code && parms->stopServer) {
1267         struct rx_connection *conn;
1268         conn =
1269             rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1270                              sc, scIndex);
1271         if (conn) {
1272             code = RXKST_Kill(conn);
1273             if (code) {
1274                 afs_com_err(whoami, code, "trying to stop server");
1275             }
1276             rx_DestroyConnection(conn);
1277         } else
1278             code = RXKST_NEWCONNFAILED;
1279     }
1280
1281     if (parms->printStats) {
1282         rx_PrintStats(stdout);
1283     }
1284
1285     rxs_Release(sc);
1286     rx_Finalize();
1287     if (code) {
1288         afs_com_err(parms->whoami, code, "test fails");
1289         exit(13);
1290     } else {
1291         printf("Test Okay\n");
1292         if (!parms->noExit)
1293             exit(0);
1294     }
1295     return 0;
1296 }