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