1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char *sccsid = "@(#)optim.c 5.5 (Berkeley) 11/2/85"; 9: #endif not lint 10: 11: /* 12: * Mail -- a program for sending and receiving mail. 13: * 14: * Network name modification routines. 15: */ 16: 17: #include "rcv.h" 18: #include "configdefs.h" 19: #include <ctype.h> 20: 21: /* 22: * Map a name into the correct network "view" of the 23: * name. This is done by prepending the name with the 24: * network address of the sender, then optimizing away 25: * nonsense. 26: */ 27: 28: char * 29: netmap(name, from) 30: char name[], from[]; 31: { 32: char nbuf[BUFSIZ], ret[BUFSIZ]; 33: register char *cp; 34: 35: if (strlen(from) == 0) 36: return(name); 37: if (any('@', name) || any('%', name)) 38: return(savestr(arpafix(name, from))); 39: cp = revarpa(from); 40: if (cp == NOSTR) 41: return(name); 42: strcpy(nbuf, cp); 43: cp = &nbuf[strlen(nbuf) - 1]; 44: while (!any(*cp, metanet) && cp > nbuf) 45: cp--; 46: if (cp == nbuf) 47: return(name); 48: *++cp = 0; 49: strcat(nbuf, revarpa(name)); 50: optim(nbuf, ret); 51: cp = revarpa(ret); 52: if (!icequal(name, cp)) 53: return(savestr(cp)); 54: return(name); 55: } 56: 57: /* 58: * Rename the given network path to use 59: * the kinds of names that we would right here. 60: */ 61: 62: char * 63: rename(str) 64: char str[]; 65: { 66: register char *cp, *cp2; 67: char buf[BUFSIZ], path[BUFSIZ]; 68: register int c, host; 69: 70: cp = str; 71: strcpy(path, ""); 72: for (;;) { 73: if ((c = *cp++) == 0) 74: break; 75: cp2 = buf; 76: while (!any(c, metanet) && c != 0) { 77: *cp2++ = c; 78: c = *cp++; 79: } 80: *cp2 = 0; 81: if (c == 0) { 82: strcat(path, buf); 83: break; 84: } 85: host = netlook(buf, ntype(c)); 86: strcat(path, netname(host)); 87: stradd(path, c); 88: } 89: if (strcmp(str, path) != 0) 90: return(savestr(path)); 91: return(str); 92: } 93: 94: /* 95: * Turn a network machine name into a unique character 96: */ 97: netlook(machine, attnet) 98: char machine[]; 99: { 100: register struct netmach *np; 101: register char *cp, *cp2; 102: char nbuf[BUFSIZ]; 103: 104: /* 105: * Make into lower case. 106: */ 107: 108: for (cp = machine, cp2 = nbuf; *cp; *cp2++ = little(*cp++)) 109: if (cp2 >= &nbuf[sizeof(nbuf)-1]) 110: break; 111: *cp2 = 0; 112: 113: /* 114: * If a single letter machine, look through those first. 115: */ 116: 117: if (strlen(nbuf) == 1) 118: for (np = netmach; np->nt_mid != 0; np++) 119: if (np->nt_mid == nbuf[0]) 120: return(nbuf[0]); 121: 122: /* 123: * Look for usual name 124: */ 125: 126: for (np = netmach; np->nt_mid != 0; np++) 127: if (strcmp(np->nt_machine, nbuf) == 0) 128: return(np->nt_mid); 129: 130: /* 131: * Look in side hash table. 132: */ 133: 134: return(mstash(nbuf, attnet)); 135: } 136: 137: /* 138: * Make a little character. 139: */ 140: 141: little(c) 142: register int c; 143: { 144: 145: if (c >= 'A' && c <= 'Z') 146: c += 'a' - 'A'; 147: return(c); 148: } 149: 150: /* 151: * Turn a network unique character identifier into a network name. 152: */ 153: 154: char * 155: netname(mid) 156: { 157: register struct netmach *np; 158: char *mlook(); 159: 160: if (mid & 0200) 161: return(mlook(mid)); 162: for (np = netmach; np->nt_mid != 0; np++) 163: if (np->nt_mid == mid) 164: return(np->nt_machine); 165: return(NOSTR); 166: } 167: 168: /* 169: * Deal with arpa net addresses. The way this is done is strange. 170: * In particular, if the destination arpa net host is not Berkeley, 171: * then the address is correct as stands. Otherwise, we strip off 172: * the trailing @Berkeley, then cook up a phony person for it to 173: * be from and optimize the result. 174: */ 175: char * 176: arpafix(name, from) 177: char name[]; 178: char from[]; 179: { 180: register char *cp; 181: register int arpamach; 182: char newname[BUFSIZ]; 183: char fake[5]; 184: char fakepath[20]; 185: 186: if (debug) { 187: fprintf(stderr, "arpafix(%s, %s)\n", name, from); 188: } 189: cp = rindex(name, '@'); 190: if (cp == NOSTR) 191: cp = rindex(name, '%'); 192: if (cp == NOSTR) { 193: fprintf(stderr, "Somethings amiss -- no @ or % in arpafix\n"); 194: return(name); 195: } 196: cp++; 197: arpamach = netlook(cp, '@'); 198: if (arpamach == 0) { 199: if (debug) 200: fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name); 201: return(name); 202: } 203: if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) { 204: if (debug) 205: fprintf(stderr, "machine %s known but remote, uses: %s\n", 206: cp, name); 207: return(name); 208: } 209: strcpy(newname, name); 210: cp = rindex(newname, '@'); 211: if (cp == NOSTR) 212: cp = rindex(newname, '%'); 213: *cp = 0; 214: fake[0] = arpamach; 215: fake[1] = ':'; 216: fake[2] = LOCAL; 217: fake[3] = ':'; 218: fake[4] = 0; 219: prefer(fake); 220: strcpy(fakepath, netname(fake[0])); 221: stradd(fakepath, fake[1]); 222: strcat(fakepath, "daemon"); 223: if (debug) 224: fprintf(stderr, "machine local, call netmap(%s, %s)\n", 225: newname, fakepath); 226: return(netmap(newname, fakepath)); 227: } 228: 229: /* 230: * Take a network machine descriptor and find the types of connected 231: * nets and return it. 232: */ 233: 234: nettype(mid) 235: { 236: register struct netmach *np; 237: 238: if (mid & 0200) 239: return(mtype(mid)); 240: for (np = netmach; np->nt_mid != 0; np++) 241: if (np->nt_mid == mid) 242: return(np->nt_type); 243: return(0); 244: } 245: 246: /* 247: * Hashing routines to salt away machines seen scanning 248: * networks paths that we don't know about. 249: */ 250: 251: #define XHSIZE 19 /* Size of extra hash table */ 252: #define NXMID (XHSIZE*3/4) /* Max extra machines */ 253: 254: struct xtrahash { 255: char *xh_name; /* Name of machine */ 256: short xh_mid; /* Machine ID */ 257: short xh_attnet; /* Attached networks */ 258: } xtrahash[XHSIZE]; 259: 260: struct xtrahash *xtab[XHSIZE]; /* F: mid-->machine name */ 261: 262: short midfree; /* Next free machine id */ 263: 264: /* 265: * Initialize the extra host hash table. 266: * Called by sreset. 267: */ 268: 269: minit() 270: { 271: register struct xtrahash *xp, **tp; 272: register int i; 273: 274: midfree = 0; 275: tp = &xtab[0]; 276: for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) { 277: xp->xh_name = NOSTR; 278: xp->xh_mid = 0; 279: xp->xh_attnet = 0; 280: *tp++ = (struct xtrahash *) 0; 281: } 282: } 283: 284: /* 285: * Stash a net name in the extra host hash table. 286: * If a new entry is put in the hash table, deduce what 287: * net the machine is attached to from the net character. 288: * 289: * If the machine is already known, add the given attached 290: * net to those already known. 291: */ 292: 293: mstash(name, attnet) 294: char name[]; 295: { 296: register struct xtrahash *xp; 297: struct xtrahash *xlocate(); 298: int x; 299: 300: xp = xlocate(name); 301: if (xp == (struct xtrahash *) 0) { 302: printf("Ran out of machine id spots\n"); 303: return(0); 304: } 305: if (xp->xh_name == NOSTR) { 306: if (midfree >= XHSIZE) { 307: printf("Out of machine ids\n"); 308: return(0); 309: } 310: xtab[midfree] = xp; 311: xp->xh_name = savestr(name); 312: xp->xh_mid = 0200 + midfree++; 313: } 314: x = ntype(attnet); 315: if (x == 0) 316: xp->xh_attnet |= SN; 317: else 318: xp->xh_attnet |= x; 319: return(xp->xh_mid); 320: } 321: 322: /* 323: * Search for the given name in the hash table 324: * and return the pointer to it if found, or to the first 325: * empty slot if not found. 326: * 327: * If no free slots can be found, return 0. 328: */ 329: 330: struct xtrahash * 331: xlocate(name) 332: char name[]; 333: { 334: register int h, q, i; 335: register char *cp; 336: register struct xtrahash *xp; 337: 338: for (h = 0, cp = name; *cp; h = (h << 2) + *cp++) 339: ; 340: if (h < 0 && (h = -h) < 0) 341: h = 0; 342: h = h % XHSIZE; 343: cp = name; 344: for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) { 345: xp = &xtrahash[(h + q) % XHSIZE]; 346: if (xp->xh_name == NOSTR) 347: return(xp); 348: if (strcmp(cp, xp->xh_name) == 0) 349: return(xp); 350: if (h - q < 0) 351: h += XHSIZE; 352: xp = &xtrahash[(h - q) % XHSIZE]; 353: if (xp->xh_name == NOSTR) 354: return(xp); 355: if (strcmp(cp, xp->xh_name) == 0) 356: return(xp); 357: } 358: return((struct xtrahash *) 0); 359: } 360: 361: /* 362: * Return the name from the extra host hash table corresponding 363: * to the passed machine id. 364: */ 365: 366: char * 367: mlook(mid) 368: { 369: register int m; 370: 371: if ((mid & 0200) == 0) 372: return(NOSTR); 373: m = mid & 0177; 374: if (m >= midfree) { 375: printf("Use made of undefined machine id\n"); 376: return(NOSTR); 377: } 378: return(xtab[m]->xh_name); 379: } 380: 381: /* 382: * Return the bit mask of net's that the given extra host machine 383: * id has so far. 384: */ 385: 386: mtype(mid) 387: { 388: register int m; 389: 390: if ((mid & 0200) == 0) 391: return(0); 392: m = mid & 0177; 393: if (m >= midfree) { 394: printf("Use made of undefined machine id\n"); 395: return(0); 396: } 397: return(xtab[m]->xh_attnet); 398: } 399: 400: /* 401: * Take a network name and optimize it. This gloriously messy 402: * operation takes place as follows: the name with machine names 403: * in it is tokenized by mapping each machine name into a single 404: * character machine id (netlook). The separator characters (network 405: * metacharacters) are left intact. The last component of the network 406: * name is stripped off and assumed to be the destination user name -- 407: * it does not participate in the optimization. As an example, the 408: * name "research!vax135!research!ucbvax!bill" becomes, tokenized, 409: * "r!x!r!v!" and "bill" A low level routine, optim1, fixes up the 410: * network part (eg, "r!x!r!v!"), then we convert back to network 411: * machine names and tack the user name on the end. 412: * 413: * The result of this is copied into the parameter "name" 414: */ 415: 416: optim(net, name) 417: char net[], name[]; 418: { 419: char netcomp[BUFSIZ], netstr[40], xfstr[40]; 420: register char *cp, *cp2; 421: register int c; 422: 423: strcpy(netstr, ""); 424: cp = net; 425: for (;;) { 426: /* 427: * Rip off next path component into netcomp 428: */ 429: cp2 = netcomp; 430: while (*cp && !any(*cp, metanet)) 431: *cp2++ = *cp++; 432: *cp2 = 0; 433: /* 434: * If we hit null byte, then we just scanned 435: * the destination user name. Go off and optimize 436: * if its so. 437: */ 438: if (*cp == 0) 439: break; 440: if ((c = netlook(netcomp, *cp)) == 0) { 441: printf("No host named \"%s\"\n", netcomp); 442: err: 443: strcpy(name, net); 444: return; 445: } 446: stradd(netstr, c); 447: stradd(netstr, *cp++); 448: /* 449: * If multiple network separators given, 450: * throw away the extras. 451: */ 452: while (any(*cp, metanet)) 453: cp++; 454: } 455: if (strlen(netcomp) == 0) { 456: printf("net name syntax\n"); 457: goto err; 458: } 459: optim1(netstr, xfstr); 460: 461: /* 462: * Convert back to machine names. 463: */ 464: 465: cp = xfstr; 466: strcpy(name, ""); 467: while (*cp) { 468: if ((cp2 = netname(*cp++)) == NOSTR) { 469: printf("Made up bad net name\n"); 470: printf("Machine code %c (0%o)\n", cp[-1], cp[-1]); 471: printf("Sorry -- dumping now. Alert K. Shoens\n"); 472: core(0); 473: goto err; 474: } 475: strcat(name, cp2); 476: stradd(name, *cp++); 477: } 478: strcat(name, netcomp); 479: } 480: 481: /* 482: * Take a string of network machine id's and separators and 483: * optimize them. We process these by pulling off maximal 484: * leading strings of the same type, passing these to the appropriate 485: * optimizer and concatenating the results. 486: */ 487: 488: optim1(netstr, name) 489: char netstr[], name[]; 490: { 491: char path[40], rpath[40]; 492: register char *cp, *cp2; 493: register int tp, nc; 494: 495: cp = netstr; 496: prefer(cp); 497: strcpy(name, ""); 498: /* 499: * If the address ultimately points back to us, 500: * just return a null network path. 501: */ 502: if (strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL) 503: return; 504: while (*cp != 0) { 505: strcpy(path, ""); 506: tp = ntype(cp[1]); 507: nc = cp[1]; 508: while (*cp && tp == ntype(cp[1])) { 509: stradd(path, *cp++); 510: cp++; 511: } 512: switch (netkind(tp)) { 513: default: 514: strcpy(rpath, path); 515: break; 516: 517: case IMPLICIT: 518: optimimp(path, rpath); 519: break; 520: 521: case EXPLICIT: 522: optimex(path, rpath); 523: break; 524: } 525: for (cp2 = rpath; *cp2 != 0; cp2++) { 526: stradd(name, *cp2); 527: stradd(name, nc); 528: } 529: } 530: optiboth(name); 531: prefer(name); 532: } 533: 534: /* 535: * Return the network of the separator -- 536: * AN for arpa net 537: * BN for Bell labs net 538: * SN for Schmidt (berkeley net) 539: * 0 if we don't know. 540: */ 541: 542: ntype(nc) 543: register int nc; 544: { 545: register struct ntypetab *np; 546: 547: for (np = ntypetab; np->nt_char != 0; np++) 548: if (np->nt_char == nc) 549: return(np->nt_bcode); 550: return(0); 551: } 552: 553: /* 554: * Return the kind of routing used for the particular net 555: * EXPLICIT means explicitly routed 556: * IMPLICIT means implicitly routed 557: * 0 means don't know 558: */ 559: 560: netkind(nt) 561: register int nt; 562: { 563: register struct nkindtab *np; 564: 565: for (np = nkindtab; np->nk_type != 0; np++) 566: if (np->nk_type == nt) 567: return(np->nk_kind); 568: return(0); 569: } 570: 571: /* 572: * Do name optimization for an explicitly routed network (eg BTL network). 573: */ 574: 575: optimex(net, name) 576: char net[], name[]; 577: { 578: register char *cp, *rp; 579: register int m; 580: char *rindex(); 581: 582: strcpy(name, net); 583: cp = name; 584: if (strlen(cp) == 0) 585: return(-1); 586: if (cp[strlen(cp)-1] == LOCAL) { 587: name[0] = 0; 588: return(0); 589: } 590: for (cp = name; *cp; cp++) { 591: m = *cp; 592: rp = rindex(cp+1, m); 593: if (rp != NOSTR) 594: strcpy(cp, rp); 595: } 596: return(0); 597: } 598: 599: /* 600: * Do name optimization for implicitly routed network (eg, arpanet, 601: * Berkeley network) 602: */ 603: 604: optimimp(net, name) 605: char net[], name[]; 606: { 607: register char *cp; 608: register int m; 609: 610: cp = net; 611: if (strlen(cp) == 0) 612: return(-1); 613: m = cp[strlen(cp) - 1]; 614: if (m == LOCAL) { 615: strcpy(name, ""); 616: return(0); 617: } 618: name[0] = m; 619: name[1] = 0; 620: return(0); 621: } 622: 623: /* 624: * Perform global optimization on the given network path. 625: * The trick here is to look ahead to see if there are any loops 626: * in the path and remove them. The interpretation of loops is 627: * more strict here than in optimex since both the machine and net 628: * type must match. 629: */ 630: 631: optiboth(net) 632: char net[]; 633: { 634: register char *cp, *cp2; 635: char *rpair(); 636: 637: cp = net; 638: if (strlen(cp) == 0) 639: return; 640: if ((strlen(cp) % 2) != 0) { 641: printf("Strange arg to optiboth\n"); 642: return; 643: } 644: while (*cp) { 645: cp2 = rpair(cp+2, *cp); 646: if (cp2 != NOSTR) 647: strcpy(cp, cp2); 648: cp += 2; 649: } 650: } 651: 652: /* 653: * Find the rightmost instance of the given (machine, type) pair. 654: */ 655: 656: char * 657: rpair(str, mach) 658: char str[]; 659: { 660: register char *cp, *last; 661: 662: cp = str; 663: last = NOSTR; 664: while (*cp) { 665: if (*cp == mach) 666: last = cp; 667: cp += 2; 668: } 669: return(last); 670: } 671: 672: /* 673: * Change the network separators in the given network path 674: * to the preferred network transmission means. 675: */ 676: 677: prefer(name) 678: char name[]; 679: { 680: register char *cp; 681: register int state, n; 682: 683: state = LOCAL; 684: for (cp = name; *cp; cp += 2) { 685: n = best(state, *cp); 686: if (n) 687: cp[1] = n; 688: state = *cp; 689: } 690: } 691: 692: /* 693: * Return the best network separator for the given machine pair. 694: */ 695: 696: best(src, dest) 697: { 698: register int dtype, stype; 699: register struct netorder *np; 700: 701: stype = nettype(src); 702: dtype = nettype(dest); 703: fflush(stdout); 704: if (stype == 0 || dtype == 0) { 705: printf("ERROR: unknown internal machine id\n"); 706: return(0); 707: } 708: if ((stype & dtype) == 0) 709: return(0); 710: np = &netorder[0]; 711: while ((np->no_stat & stype & dtype) == 0) 712: np++; 713: return(np->no_char); 714: } 715: 716: #ifdef GETHOST 717: /* 718: * Initialize the network name of the current host. 719: */ 720: inithost() 721: { 722: register struct netmach *np; 723: static char host[64]; 724: 725: gethostname(host, sizeof host); 726: for (np = netmach; np->nt_machine != 0; np++) 727: if (strcmp(np->nt_machine, EMPTY) == 0) 728: break; 729: if (np->nt_machine == 0) { 730: printf("Cannot find empty slot for dynamic host entry\n"); 731: exit(1); 732: } 733: np->nt_machine = host; 734: } 735: #endif GETHOST 736: 737: /* 738: * Code to twist around arpa net names. 739: */ 740: 741: #define WORD 257 /* Token for a string */ 742: 743: static char netbuf[256]; 744: static char *yylval; 745: 746: /* 747: * Reverse all of the arpa net addresses in the given name to 748: * be of the form "host @ user" instead of "user @ host" 749: * This function is its own inverse. 750: */ 751: 752: char * 753: revarpa(str) 754: char str[]; 755: { 756: 757: if (yyinit(str) < 0) 758: return(NOSTR); 759: if (name()) 760: return(NOSTR); 761: if (strcmp(str, netbuf) == 0) 762: return(str); 763: return(savestr(netbuf)); 764: } 765: 766: /* 767: * Parse (by recursive descent) network names, using the following grammar: 768: * name: 769: * term {':' term} 770: * term {'^' term} 771: * term {'!' term} 772: * term '@' name 773: * term '%' name 774: * 775: * term: 776: * string of characters. 777: */ 778: 779: name() 780: { 781: register int t; 782: register char *cp; 783: 784: for (;;) { 785: t = yylex(); 786: if (t != WORD) 787: return(-1); 788: cp = yylval; 789: t = yylex(); 790: switch (t) { 791: case 0: 792: strcat(netbuf, cp); 793: return(0); 794: 795: case '@': 796: case '%': 797: if (name()) 798: return(-1); 799: stradd(netbuf, '@'); 800: strcat(netbuf, cp); 801: return(0); 802: 803: case WORD: 804: return(-1); 805: 806: default: 807: strcat(netbuf, cp); 808: stradd(netbuf, t); 809: } 810: } 811: } 812: 813: /* 814: * Scanner for network names. 815: */ 816: 817: static char *charp; /* Current input pointer */ 818: static int nexttok; /* Salted away next token */ 819: 820: /* 821: * Initialize the network name scanner. 822: */ 823: 824: yyinit(str) 825: char str[]; 826: { 827: static char lexbuf[BUFSIZ]; 828: 829: netbuf[0] = 0; 830: if (strlen(str) >= sizeof lexbuf - 1) 831: return(-1); 832: nexttok = 0; 833: strcpy(lexbuf, str); 834: charp = lexbuf; 835: return(0); 836: } 837: 838: /* 839: * Scan and return a single token. 840: * yylval is set to point to a scanned string. 841: */ 842: 843: yylex() 844: { 845: register char *cp, *dot; 846: register int s; 847: 848: if (nexttok) { 849: s = nexttok; 850: nexttok = 0; 851: return(s); 852: } 853: cp = charp; 854: while (*cp && isspace(*cp)) 855: cp++; 856: if (*cp == 0) 857: return(0); 858: if (any(*cp, metanet)) { 859: charp = cp+1; 860: return(*cp); 861: } 862: dot = cp; 863: while (*cp && !any(*cp, metanet) && !any(*cp, " \t")) 864: cp++; 865: if (any(*cp, metanet)) 866: nexttok = *cp; 867: if (*cp == 0) 868: charp = cp; 869: else 870: charp = cp+1; 871: *cp = 0; 872: yylval = dot; 873: return(WORD); 874: } 875: 876: /* 877: * Add a single character onto a string. 878: */ 879: 880: stradd(str, c) 881: register char *str; 882: register int c; 883: { 884: 885: str += strlen(str); 886: *str++ = c; 887: *str = 0; 888: }