|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)optim.c 2.9 (Berkeley) 8/11/83";
3: #endif
4:
5: /*
6: * Mail -- a program for sending and receiving mail.
7: *
8: * Network name modification routines.
9: */
10:
11: #include "rcv.h"
12: #include "configdefs.h"
13: #include <ctype.h>
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.