|
|
1.1 root 1: /* Copyright (c) 1982 Regents of the University of California */
2:
3: static char sccsid[] = "@(#)ruserpass.c 4.2 10/10/82";
4:
5: #include <stdio.h>
6: #include <utmp.h>
7: #include <ctype.h>
8: #include <sys/types.h>
9: #include <sys/stat.h>
10: #include <errno.h>
11:
12: char *renvlook(), *malloc(), *index(), *getenv(), *getpass(), *getlogin();
13: struct utmp *getutmp();
14: static FILE *cfile;
15:
16: ruserpass(host, aname, apass)
17: char *host, **aname, **apass;
18: {
19:
20: renv(host, aname, apass);
21: if (*aname == 0 || *apass == 0)
22: rnetrc(host, aname, apass);
23: if (*aname == 0) {
24: char *myname = getlogin();
25: *aname = malloc(16);
26: printf("Name (%s:%s): ", host, myname);
27: fflush(stdout);
28: if (read(2, *aname, 16) <= 0)
29: exit(1);
30: if ((*aname)[0] == '\n')
31: *aname = myname;
32: else
33: if (index(*aname, '\n'))
34: *index(*aname, '\n') = 0;
35: }
36: if (*aname && *apass == 0) {
37: printf("Password (%s:%s): ", host, *aname);
38: fflush(stdout);
39: *apass = getpass("");
40: }
41: }
42:
43: static
44: renv(host, aname, apass)
45: char *host, **aname, **apass;
46: {
47: register char *cp;
48: char *stemp, fgetlogin, *comma;
49:
50: cp = renvlook(host);
51: if (cp == NULL)
52: return;
53: if (!isalpha(cp[0]))
54: return;
55: comma = index(cp, ',');
56: if (comma == 0)
57: return;
58: if (*aname == 0) {
59: *aname = malloc(comma - cp + 1);
60: strncpy(*aname, cp, comma - cp);
61: } else
62: if (strncmp(*aname, cp, comma - cp))
63: return;
64: comma++;
65: cp = malloc(strlen(comma)+1);
66: strcpy(cp, comma);
67: *apass = malloc(16);
68: mkpwclear(cp, host[0], *apass);
69: }
70:
71: static
72: char *
73: renvlook(host)
74: char *host;
75: {
76: register char *cp, **env;
77: extern char **environ;
78:
79: env = environ;
80: for (env = environ; *env != NULL; env++)
81: if (!strncmp(*env, "MACH", 4)) {
82: cp = index(*env, '=');
83: if (cp == 0)
84: continue;
85: if (strncmp(*env+4, host, cp-(*env+4)))
86: continue;
87: return (cp+1);
88: }
89: return (NULL);
90: }
91:
92: #define DEFAULT 1
93: #define LOGIN 2
94: #define PASSWD 3
95: #define NOTIFY 4
96: #define WRITE 5
97: #define YES 6
98: #define NO 7
99: #define COMMAND 8
100: #define FORCE 9
101: #define ID 10
102: #define MACHINE 11
103:
104: static char tokval[100];
105:
106: static struct toktab {
107: char *tokstr;
108: int tval;
109: } toktab[]= {
110: "default", DEFAULT,
111: "login", LOGIN,
112: "password", PASSWD,
113: "notify", NOTIFY,
114: "write", WRITE,
115: "yes", YES,
116: "y", YES,
117: "no", NO,
118: "n", NO,
119: "command", COMMAND,
120: "force", FORCE,
121: "machine", MACHINE,
122: 0, 0
123: };
124:
125: static
126: rnetrc(host, aname, apass)
127: char *host, **aname, **apass;
128: {
129: char *hdir, buf[BUFSIZ];
130: int t;
131: struct stat stb;
132: extern int errno;
133:
134: hdir = getenv("HOME");
135: if (hdir == NULL)
136: hdir = ".";
137: sprintf(buf, "%s/.netrc", hdir);
138: cfile = fopen(buf, "r");
139: if (cfile == NULL) {
140: if (errno != ENOENT)
141: perror(buf);
142: return;
143: }
144: next:
145: while ((t = token())) switch(t) {
146:
147: case DEFAULT:
148: (void) token();
149: continue;
150:
151: case MACHINE:
152: if (token() != ID || strcmp(host, tokval))
153: continue;
154: while ((t = token()) && t != MACHINE) switch(t) {
155:
156: case LOGIN:
157: if (token())
158: if (*aname == 0) {
159: *aname = malloc(strlen(tokval) + 1);
160: strcpy(*aname, tokval);
161: } else {
162: if (strcmp(*aname, tokval))
163: goto next;
164: }
165: break;
166: case PASSWD:
167: if (fstat(fileno(cfile), &stb) >= 0
168: && (stb.st_mode & 077) != 0) {
169: fprintf(stderr, "Error - .netrc file not correct mode.\n");
170: fprintf(stderr, "Remove password or correct mode.\n");
171: exit(1);
172: }
173: if (token() && *apass == 0) {
174: *apass = malloc(strlen(tokval) + 1);
175: strcpy(*apass, tokval);
176: }
177: break;
178: case COMMAND:
179: case NOTIFY:
180: case WRITE:
181: case FORCE:
182: (void) token();
183: break;
184: default:
185: fprintf(stderr, "Unknown .netrc option %s\n", tokval);
186: break;
187: }
188: goto done;
189: }
190: done:
191: fclose(cfile);
192: }
193:
194: static
195: token()
196: {
197: char *cp;
198: int c;
199: struct toktab *t;
200:
201: if (feof(cfile))
202: return (0);
203: while ((c = getc(cfile)) != EOF &&
204: (c == '\n' || c == '\t' || c == ' ' || c == ','))
205: continue;
206: if (c == EOF)
207: return (0);
208: cp = tokval;
209: if (c == '"') {
210: while ((c = getc(cfile)) != EOF && c != '"') {
211: if (c == '\\')
212: c = getc(cfile);
213: *cp++ = c;
214: }
215: } else {
216: *cp++ = c;
217: while ((c = getc(cfile)) != EOF
218: && c != '\n' && c != '\t' && c != ' ' && c != ',') {
219: if (c == '\\')
220: c = getc(cfile);
221: *cp++ = c;
222: }
223: }
224: *cp = 0;
225: if (tokval[0] == 0)
226: return (0);
227: for (t = toktab; t->tokstr; t++)
228: if (!strcmp(t->tokstr, tokval))
229: return (t->tval);
230: return (ID);
231: }
232: /* rest is nbs.c stolen from berknet */
233:
234: char *deblknot(), *deblkclr();
235: char *nbs8decrypt(), *nbs8encrypt();
236: static char E[48];
237:
238: /*
239: * The E bit-selection table.
240: */
241: static char e[] = {
242: 32, 1, 2, 3, 4, 5,
243: 4, 5, 6, 7, 8, 9,
244: 8, 9,10,11,12,13,
245: 12,13,14,15,16,17,
246: 16,17,18,19,20,21,
247: 20,21,22,23,24,25,
248: 24,25,26,27,28,29,
249: 28,29,30,31,32, 1,
250: };
251: static
252: char *nbsencrypt(str,key,result)
253: char *result;
254: char *str, *key; {
255: static char buf[20],oldbuf[20];
256: register int j;
257: result[0] = 0;
258: strcpy(oldbuf,key);
259: while(*str){
260: for(j=0;j<10;j++)buf[j] = 0;
261: for(j=0;j<8 && *str;j++)buf[j] = *str++;
262: strcat(result,nbs8encrypt(buf,oldbuf));
263: strcat(result,"$");
264: strcpy(oldbuf,buf);
265: }
266: return(result);
267: }
268: static
269: char *nbsdecrypt(cpt,key,result)
270: char *result;
271: char *cpt,*key; {
272: char *s;
273: char c,oldbuf[20];
274: result[0] = 0;
275: strcpy(oldbuf,key);
276: while(*cpt){
277: for(s = cpt;*s && *s != '$';s++);
278: c = *s;
279: *s = 0;
280: strcpy(oldbuf,nbs8decrypt(cpt,oldbuf));
281: strcat(result,oldbuf);
282: if(c == 0)break;
283: cpt = s + 1;
284: }
285: return(result);
286: }
287:
288: static
289: char *nbs8encrypt(str,key)
290: char *str, *key; {
291: static char keyblk[100], blk[100];
292: register int i;
293:
294: enblkclr(keyblk,key);
295: nbssetkey(keyblk);
296:
297: for(i=0;i<48;i++) E[i] = e[i];
298: enblkclr(blk,str);
299: blkencrypt(blk,0); /* forward dir */
300:
301: return(deblknot(blk));
302: }
303:
304: static
305: char *nbs8decrypt(crp,key)
306: char *crp, *key; {
307: static char keyblk[100], blk[100];
308: register int i;
309:
310: enblkclr(keyblk,key);
311: nbssetkey(keyblk);
312:
313: for(i=0;i<48;i++) E[i] = e[i];
314: enblknot(blk,crp);
315: blkencrypt(blk,1); /* backward dir */
316:
317: return(deblkclr(blk));
318: }
319:
320: static
321: enblkclr(blk,str) /* ignores top bit of chars in string str */
322: char *blk,*str; {
323: register int i,j;
324: char c;
325: for(i=0;i<70;i++)blk[i] = 0;
326: for(i=0; (c= *str) && i<64; str++){
327: for(j=0; j<7; j++, i++)
328: blk[i] = (c>>(6-j)) & 01;
329: i++;
330: }
331: }
332:
333: static
334: char *deblkclr(blk)
335: char *blk; {
336: register int i,j;
337: char c;
338: static char iobuf[30];
339: for(i=0; i<10; i++){
340: c = 0;
341: for(j=0; j<7; j++){
342: c <<= 1;
343: c |= blk[8*i+j];
344: }
345: iobuf[i] = c;
346: }
347: iobuf[i] = 0;
348: return(iobuf);
349: }
350:
351: static
352: enblknot(blk,crp)
353: char *blk;
354: char *crp; {
355: register int i,j;
356: char c;
357: for(i=0;i<70;i++)blk[i] = 0;
358: for(i=0; (c= *crp) && i<64; crp++){
359: if(c>'Z') c -= 6;
360: if(c>'9') c -= 7;
361: c -= '.';
362: for(j=0; j<6; j++, i++)
363: blk[i] = (c>>(5-j)) & 01;
364: }
365: }
366:
367: static
368: char *deblknot(blk)
369: char *blk; {
370: register int i,j;
371: char c;
372: static char iobuf[30];
373: for(i=0; i<11; i++){
374: c = 0;
375: for(j=0; j<6; j++){
376: c <<= 1;
377: c |= blk[6*i+j];
378: }
379: c += '.';
380: if(c > '9')c += 7;
381: if(c > 'Z')c += 6;
382: iobuf[i] = c;
383: }
384: iobuf[i] = 0;
385: return(iobuf);
386: }
387:
388: /*
389: * This program implements the
390: * Proposed Federal Information Processing
391: * Data Encryption Standard.
392: * See Federal Register, March 17, 1975 (40FR12134)
393: */
394:
395: /*
396: * Initial permutation,
397: */
398: static char IP[] = {
399: 58,50,42,34,26,18,10, 2,
400: 60,52,44,36,28,20,12, 4,
401: 62,54,46,38,30,22,14, 6,
402: 64,56,48,40,32,24,16, 8,
403: 57,49,41,33,25,17, 9, 1,
404: 59,51,43,35,27,19,11, 3,
405: 61,53,45,37,29,21,13, 5,
406: 63,55,47,39,31,23,15, 7,
407: };
408:
409: /*
410: * Final permutation, FP = IP^(-1)
411: */
412: static char FP[] = {
413: 40, 8,48,16,56,24,64,32,
414: 39, 7,47,15,55,23,63,31,
415: 38, 6,46,14,54,22,62,30,
416: 37, 5,45,13,53,21,61,29,
417: 36, 4,44,12,52,20,60,28,
418: 35, 3,43,11,51,19,59,27,
419: 34, 2,42,10,50,18,58,26,
420: 33, 1,41, 9,49,17,57,25,
421: };
422:
423: /*
424: * Permuted-choice 1 from the key bits
425: * to yield C and D.
426: * Note that bits 8,16... are left out:
427: * They are intended for a parity check.
428: */
429: static char PC1_C[] = {
430: 57,49,41,33,25,17, 9,
431: 1,58,50,42,34,26,18,
432: 10, 2,59,51,43,35,27,
433: 19,11, 3,60,52,44,36,
434: };
435:
436: static char PC1_D[] = {
437: 63,55,47,39,31,23,15,
438: 7,62,54,46,38,30,22,
439: 14, 6,61,53,45,37,29,
440: 21,13, 5,28,20,12, 4,
441: };
442:
443: /*
444: * Sequence of shifts used for the key schedule.
445: */
446: static char shifts[] = {
447: 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1,
448: };
449:
450: /*
451: * Permuted-choice 2, to pick out the bits from
452: * the CD array that generate the key schedule.
453: */
454: static char PC2_C[] = {
455: 14,17,11,24, 1, 5,
456: 3,28,15, 6,21,10,
457: 23,19,12, 4,26, 8,
458: 16, 7,27,20,13, 2,
459: };
460:
461: static char PC2_D[] = {
462: 41,52,31,37,47,55,
463: 30,40,51,45,33,48,
464: 44,49,39,56,34,53,
465: 46,42,50,36,29,32,
466: };
467:
468: /*
469: * The C and D arrays used to calculate the key schedule.
470: */
471:
472: static char C[28];
473: static char D[28];
474: /*
475: * The key schedule.
476: * Generated from the key.
477: */
478: static char KS[16][48];
479:
480: /*
481: * Set up the key schedule from the key.
482: */
483:
484: static
485: nbssetkey(key)
486: char *key;
487: {
488: register i, j, k;
489: int t;
490:
491: /*
492: * First, generate C and D by permuting
493: * the key. The low order bit of each
494: * 8-bit char is not used, so C and D are only 28
495: * bits apiece.
496: */
497: for (i=0; i<28; i++) {
498: C[i] = key[PC1_C[i]-1];
499: D[i] = key[PC1_D[i]-1];
500: }
501: /*
502: * To generate Ki, rotate C and D according
503: * to schedule and pick up a permutation
504: * using PC2.
505: */
506: for (i=0; i<16; i++) {
507: /*
508: * rotate.
509: */
510: for (k=0; k<shifts[i]; k++) {
511: t = C[0];
512: for (j=0; j<28-1; j++)
513: C[j] = C[j+1];
514: C[27] = t;
515: t = D[0];
516: for (j=0; j<28-1; j++)
517: D[j] = D[j+1];
518: D[27] = t;
519: }
520: /*
521: * get Ki. Note C and D are concatenated.
522: */
523: for (j=0; j<24; j++) {
524: KS[i][j] = C[PC2_C[j]-1];
525: KS[i][j+24] = D[PC2_D[j]-28-1];
526: }
527: }
528: }
529:
530:
531: /*
532: * The 8 selection functions.
533: * For some reason, they give a 0-origin
534: * index, unlike everything else.
535: */
536: static char S[8][64] = {
537: 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7,
538: 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8,
539: 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0,
540: 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13,
541:
542: 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10,
543: 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5,
544: 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15,
545: 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9,
546:
547: 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8,
548: 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1,
549: 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7,
550: 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12,
551:
552: 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15,
553: 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9,
554: 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4,
555: 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14,
556:
557: 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9,
558: 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6,
559: 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14,
560: 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3,
561:
562: 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11,
563: 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8,
564: 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6,
565: 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13,
566:
567: 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1,
568: 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6,
569: 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2,
570: 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12,
571:
572: 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7,
573: 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2,
574: 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8,
575: 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11,
576: };
577:
578: /*
579: * P is a permutation on the selected combination
580: * of the current L and key.
581: */
582: static char P[] = {
583: 16, 7,20,21,
584: 29,12,28,17,
585: 1,15,23,26,
586: 5,18,31,10,
587: 2, 8,24,14,
588: 32,27, 3, 9,
589: 19,13,30, 6,
590: 22,11, 4,25,
591: };
592:
593: /*
594: * The current block, divided into 2 halves.
595: */
596: static char L[32], R[32];
597: static char tempL[32];
598: static char f[32];
599:
600: /*
601: * The combination of the key and the input, before selection.
602: */
603: static char preS[48];
604:
605: /*
606: * The payoff: encrypt a block.
607: */
608:
609: static
610: blkencrypt(block, edflag)
611: char *block;
612: {
613: int i, ii;
614: register t, j, k;
615:
616: /*
617: * First, permute the bits in the input
618: */
619: for (j=0; j<64; j++)
620: L[j] = block[IP[j]-1];
621: /*
622: * Perform an encryption operation 16 times.
623: */
624: for (ii=0; ii<16; ii++) {
625: /*
626: * Set direction
627: */
628: if (edflag)
629: i = 15-ii;
630: else
631: i = ii;
632: /*
633: * Save the R array,
634: * which will be the new L.
635: */
636: for (j=0; j<32; j++)
637: tempL[j] = R[j];
638: /*
639: * Expand R to 48 bits using the E selector;
640: * exclusive-or with the current key bits.
641: */
642: for (j=0; j<48; j++)
643: preS[j] = R[E[j]-1] ^ KS[i][j];
644: /*
645: * The pre-select bits are now considered
646: * in 8 groups of 6 bits each.
647: * The 8 selection functions map these
648: * 6-bit quantities into 4-bit quantities
649: * and the results permuted
650: * to make an f(R, K).
651: * The indexing into the selection functions
652: * is peculiar; it could be simplified by
653: * rewriting the tables.
654: */
655: for (j=0; j<8; j++) {
656: t = 6*j;
657: k = S[j][(preS[t+0]<<5)+
658: (preS[t+1]<<3)+
659: (preS[t+2]<<2)+
660: (preS[t+3]<<1)+
661: (preS[t+4]<<0)+
662: (preS[t+5]<<4)];
663: t = 4*j;
664: f[t+0] = (k>>3)&01;
665: f[t+1] = (k>>2)&01;
666: f[t+2] = (k>>1)&01;
667: f[t+3] = (k>>0)&01;
668: }
669: /*
670: * The new R is L ^ f(R, K).
671: * The f here has to be permuted first, though.
672: */
673: for (j=0; j<32; j++)
674: R[j] = L[j] ^ f[P[j]-1];
675: /*
676: * Finally, the new L (the original R)
677: * is copied back.
678: */
679: for (j=0; j<32; j++)
680: L[j] = tempL[j];
681: }
682: /*
683: * The output L and R are reversed.
684: */
685: for (j=0; j<32; j++) {
686: t = L[j];
687: L[j] = R[j];
688: R[j] = t;
689: }
690: /*
691: * The final output
692: * gets the inverse permutation of the very original.
693: */
694: for (j=0; j<64; j++)
695: block[j] = L[FP[j]-1];
696: }
697: /*
698: getutmp()
699: return a pointer to the system utmp structure associated with
700: terminal sttyname, e.g. "/dev/tty3"
701: Is version independent-- will work on v6 systems
702: return NULL if error
703: */
704: static
705: struct utmp *getutmp(sttyname)
706: char *sttyname;
707: {
708: static struct utmp utmpstr;
709: FILE *fdutmp;
710:
711: if(sttyname == NULL || sttyname[0] == 0)return(NULL);
712:
713: fdutmp = fopen("/etc/utmp","r");
714: if(fdutmp == NULL)return(NULL);
715:
716: while(fread(&utmpstr,1,sizeof utmpstr,fdutmp) == sizeof utmpstr)
717: if(strcmp(utmpstr.ut_line,sttyname+5) == 0){
718: fclose(fdutmp);
719: return(&utmpstr);
720: }
721: fclose(fdutmp);
722: return(NULL);
723: }
724:
725: static
726: sreverse(sto, sfrom)
727: register char *sto, *sfrom;
728: {
729: register int i;
730:
731: i = strlen(sfrom);
732: while (i >= 0)
733: *sto++ = sfrom[i--];
734: }
735:
736: static
737: char *mkenvkey(mch)
738: char mch;
739: {
740: static char skey[40];
741: register struct utmp *putmp;
742: char stemp[40], stemp1[40], sttyname[30];
743: register char *sk,*p;
744:
745: if (isatty(2))
746: strcpy(sttyname,ttyname(2));
747: else if (isatty(0))
748: strcpy(sttyname,ttyname(0));
749: else if (isatty(1))
750: strcpy(sttyname,ttyname(1));
751: else
752: return (NULL);
753: putmp = getutmp(sttyname);
754: if (putmp == NULL)
755: return (NULL);
756: sk = skey;
757: p = putmp->ut_line;
758: while (*p)
759: *sk++ = *p++;
760: *sk++ = mch;
761: sprintf(stemp, "%ld", putmp->ut_time);
762: sreverse(stemp1, stemp);
763: p = stemp1;
764: while (*p)
765: *sk++ = *p++;
766: *sk = 0;
767: return (skey);
768: }
769:
770: mkpwunclear(spasswd,mch,sencpasswd)
771: char mch, *spasswd, *sencpasswd;
772: {
773: register char *skey;
774:
775: if (spasswd[0] == 0) {
776: sencpasswd[0] = 0;
777: return;
778: }
779: skey = mkenvkey(mch);
780: if (skey == NULL) {
781: fprintf(stderr, "Can't make key\n");
782: exit(1);
783: }
784: nbsencrypt(spasswd, skey, sencpasswd);
785: }
786:
787: mkpwclear(sencpasswd,mch,spasswd)
788: char mch, *spasswd, *sencpasswd;
789: {
790: register char *skey;
791:
792: if (sencpasswd[0] == 0) {
793: spasswd[0] = 0;
794: return;
795: }
796: skey = mkenvkey(mch);
797: if (skey == NULL) {
798: fprintf(stderr, "Can't make key\n");
799: exit(1);
800: }
801: nbsdecrypt(sencpasswd, skey, spasswd);
802: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.