4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 1998 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
34 * mailx -- a modified version of a University of California at Berkeley
37 * Network name modification routines.
41 #include "configdefs.h"
44 static char *arpafix(char name
[], char from
[]);
45 static char *lasthost(char *addr
);
46 static char *makeremote(char name
[], char from
[]);
47 static int mstash(char name
[], int attnet
);
48 static int mtype(int mid
);
49 static int netlook(char machine
[], int attnet
);
50 static int nettype(int mid
);
51 static int ntype(register int nc
);
52 static void stradd(register char *str
, int n
, register int c
);
53 static char *tackon(char *sys
, char *rest
);
54 static struct xtrahash
*xlocate(char name
[]);
56 static char best(int src
, int dest
);
57 static char *mlook(int mid
);
58 static int netkind(register int nt
);
59 static void optiboth(char net
[]);
60 static void optim(char net
[], char name
[]);
61 static void optim1(char netstr
[], char name
[]);
62 static int optimex(char net
[], char name
[]);
63 static int optimimp(char net
[], char name
[]);
64 static void prefer(char name
[]);
65 static char *rpair(char str
[], int mach
);
69 * Map a name into the correct network "view" of the
70 * name. This is done by prepending the name with the
71 * network address of the sender, then optimizing away
76 netmap(char name
[], char from
[])
78 char nbuf
[BUFSIZ
], ret
[BUFSIZ
];
79 register char *cp
, *oname
;
81 if (debug
) fprintf(stderr
, "netmap(name '%s', from '%s')\n", name
, from
);
82 if (strlen(from
) == 0)
83 return(name
); /* "from" is empty - can't do anything */
85 if (strcmp(from
, name
) == 0)
86 return(name
); /* "from" and "name" are the same, do nothing */
89 * If the name contains an "@" or a "%", remove it and the host
90 * following it if that host is "known".
92 if (any('@', name
) || any('%', name
))
93 return(arpafix(name
, from
));
96 * If the sender contains a "@" or a "%", make "name" into an
97 * address on that host, on the presumption that it should
98 * really have read "name@from" when we received the message
99 * rather than just "name".
101 if (any('@', from
) || any('%', from
))
102 return(unuucp(makeremote(name
, from
)));
103 if (value("onehop") && (cp
= strchr(name
, '!')) && cp
> name
) {
105 * "onehop" is set, meaning all machines are one UUCP
106 * hop away (fat chance, in this day and age), and "name"
107 * is a UUCP path rather than just a name. Leave it alone.
109 nstrcpy(nbuf
, sizeof (nbuf
), name
);
111 from
= tackon(host
, from
);
112 *strrchr(from
, '!') = 0;
113 name
= tackon(lasthost(from
), name
);
114 while (((cp
= lasthost(from
)) != 0) && ishost(cp
, name
)) {
116 name
= strchr(name
, '!') + 1;
118 from
[strlen(from
)] = '!';
119 if (value("mustbang") && !strchr(name
, '!'))
121 return(unuucp(name
));
125 from
[strlen(from
)] = '!';
126 from
= strchr(from
, '!') + 1;
127 snprintf(nbuf
, sizeof (nbuf
), "%s!%s", from
, name
);
129 if (debug
) fprintf(stderr
, "before optim, nbuf '%s'\n", name
);
131 if ((cp
= value("conv"))==NOSTR
|| strcmp(cp
, "optimize") != 0)
132 nstrcpy(ret
, sizeof (ret
), nbuf
);
136 nstrcpy(ret
, sizeof (ret
), nbuf
);
138 if (debug
) fprintf(stderr
, "after optim, nbuf '%s', ret '%s'\n", nbuf
, ret
);
140 if (debug
) fprintf(stderr
, "wind up with '%s'\n", name
);
141 if (!icequal(name
, cp
))
142 return(unuucp((char *) savestr(cp
)));
143 return(unuucp(name
));
147 * Stick a host on the beginning of a uucp
148 * address if it isn't there already.
151 tackon(char *sys
, char *rest
)
155 if (!ishost(sys
, rest
)) {
156 char *r
= (char *)salloc(strlen(sys
) + strlen(rest
) + 2);
157 sprintf(r
, "%s!%s", sys
, rest
);
164 * Check equality of the first host in a uucp address.
167 ishost(char *sys
, char *rest
)
169 while (*sys
&& *sys
== *rest
)
171 return(*sys
== 0 && *rest
== '!');
175 * Return last host in a uucp address.
180 char *r
= strrchr(addr
, '!');
181 return r
? ++r
: addr
;
185 * Optionally translate an old format uucp name into a new one, e.g.
186 * "mach1!mach2!user" becomes "user@mach2.UUCP". This optional because
187 * some information is necessarily lost (e.g. the route it got here
188 * via) and if we don't have the host in our routing tables, we lose.
189 * XXX THIS IS NO LONGER VALID WITH THE NEW UUCP PROJECT PLANS TO
190 * REGISTER UUCP HOSTS IN THE STANDARD INTERNET NAMESPACE, E.G.
191 * ihnp4 BECOMES "ihnp4.att.com".
196 register char *np
, *hp
, *cp
;
201 ((cp
= value("conv"))==NOSTR
|| strcmp(cp
, "internet")))
203 if (debug
) fprintf(stderr
, "unuucp(%s)\n", name
);
204 nstrcpy(tname
, sizeof (tname
), name
);
205 np
= strrchr(tname
, '!');
209 hp
= strrchr(tname
, '!');
214 cp
= strchr(np
, '@');
216 cp
= strchr(np
, '%');
219 if (debug
) fprintf(stderr
, "host %s, name %s\n", hp
, np
);
220 snprintf(result
, sizeof (result
), "%s@%s.UUCP", np
, hp
);
221 if (debug
) fprintf(stderr
, "unuucp returns %s\n", result
);
222 return savestr(result
);
226 * Turn a network machine name into a unique character
229 netlook(char machine
[], int attnet
)
231 register struct netmach
*np
;
232 register char *cp
, *cp2
;
236 * Make into lower case.
238 for (cp
= machine
, cp2
= nbuf
;
239 *cp
&& cp2
< &nbuf
[BUFSIZ
-1];
240 *cp2
++ = tolower(*cp
++))
245 * If a single letter machine, look through those first.
248 if (strlen(nbuf
) == 1)
249 for (np
= netmach
; np
->nt_mid
!= 0; np
++)
250 if (np
->nt_mid
== nbuf
[0])
254 * Look for usual name
257 for (np
= netmach
; np
->nt_mid
!= 0; np
++)
258 if (strcmp(np
->nt_machine
, nbuf
) == 0)
262 * Look in side hash table.
265 return(mstash(nbuf
, attnet
));
270 * Turn a network unique character identifier into a network name.
276 register struct netmach
*np
;
280 for (np
= netmach
; np
->nt_mid
!= 0; np
++)
281 if (np
->nt_mid
== mid
)
282 return(np
->nt_machine
);
288 * Deal with arpa net addresses. The way this is done is strange.
289 * name contains an "@" or "%". Look up the machine after it in
290 * the hash table. If it isn't found, return name unmolested.
291 * If ???, return name unmolested.
292 * Otherwise, delete the "@" or "%" and the machine after it from
293 * name, and return the new string.
296 arpafix(char name
[], char from
[])
299 register int arpamach
;
300 char newname
[BUFSIZ
];
303 fprintf(stderr
, "arpafix(%s, %s)\n", name
, from
);
305 cp
= strrchr(name
, '@');
307 cp
= strrchr(name
, '%');
310 gettext("Something's amiss -- no @ or %% in arpafix\n"));
314 arpamach
= netlook(cp
, '@');
317 "cp '%s', arpamach %o, nettypes arpamach %o LOCAL %o\n",
318 cp
, arpamach
, nettype(arpamach
), nettype(LOCAL
));
321 fprintf(stderr
, "machine %s unknown, uses: %s\n",
325 if (((nettype(arpamach
) & nettype(LOCAL
)) & ~AN
) == 0) {
327 fprintf(stderr
, "machine %s known but remote, uses: %s\n",
331 nstrcpy(newname
, sizeof (newname
), name
);
332 cp
= strrchr(newname
, '@');
334 cp
= strrchr(newname
, '%');
336 if (debug
) fprintf(stderr
, "local address, return '%s'\n", newname
);
337 return(savestr(newname
));
341 * We have name with no @'s in it, and from with @'s.
342 * Assume that name is meaningful only on the site in from,
343 * and return "name@site_in_from".
346 makeremote(char name
[], char from
[])
351 if (!value("makeremote"))
353 if (debug
) fprintf(stderr
, "makeremote(%s, %s) returns ", name
, from
);
354 cp
= strrchr(from
, '@');
356 cp
= strrchr(from
, '%');
357 snprintf(rbuf
, sizeof (rbuf
), "%s%s", name
, cp
);
358 if (debug
) fprintf(stderr
, "%s\n", rbuf
);
359 return(savestr(rbuf
));
363 * Take a network machine descriptor and find the types of connected
364 * nets and return it.
369 register struct netmach
*np
;
373 for (np
= netmach
; np
->nt_mid
!= 0; np
++)
374 if (np
->nt_mid
== mid
)
380 * Hashing routines to salt away machines seen scanning
381 * networks paths that we don't know about.
384 #define XHSIZE 97 /* Size of extra hash table */
385 #define NXMID (XHSIZE*3/4) /* Max extra machines */
388 char *xh_name
; /* Name of machine */
389 short xh_mid
; /* Machine ID */
390 short xh_attnet
; /* Attached networks */
393 static struct xtrahash
*xtab
[XHSIZE
]; /* F: mid-->machine name */
395 static short midfree
; /* Next free machine id */
398 * Initialize the extra host hash table.
404 register struct xtrahash
*xp
, **tp
;
408 for (xp
= &xtrahash
[0]; xp
< &xtrahash
[XHSIZE
]; xp
++) {
412 *tp
++ = (struct xtrahash
*) 0;
417 * Stash a net name in the extra host hash table.
418 * If a new entry is put in the hash table, deduce what
419 * net the machine is attached to from the net character.
421 * If the machine is already known, add the given attached
422 * net to those already known.
425 mstash(char name
[], int attnet
)
427 register struct xtrahash
*xp
;
431 if (xp
== (struct xtrahash
*) 0) {
432 printf(gettext("Ran out of machine id spots\n"));
435 if (xp
->xh_name
== NOSTR
) {
436 if (midfree
>= XHSIZE
) {
437 printf(gettext("Out of machine ids\n"));
441 xp
->xh_name
= savestr(name
);
442 xp
->xh_mid
= 0200 + midfree
++;
453 * Search for the given name in the hash table
454 * and return the pointer to it if found, or to the first
455 * empty slot if not found.
457 * If no free slots can be found, return 0.
460 static struct xtrahash
*
463 register int h
, q
, i
;
465 register struct xtrahash
*xp
;
467 for (h
= 0, cp
= name
; *cp
; h
= (h
<< 2) + *cp
++)
469 if (h
< 0 && (h
= -h
) < 0)
473 for (i
= 0, q
= 0; q
< XHSIZE
; i
++, q
= i
* i
) {
474 xp
= &xtrahash
[(h
+ q
) % XHSIZE
];
475 if (xp
->xh_name
== NOSTR
)
477 if (strcmp(cp
, xp
->xh_name
) == 0)
481 xp
= &xtrahash
[(h
- q
) % XHSIZE
];
482 if (xp
->xh_name
== NOSTR
)
484 if (strcmp(cp
, xp
->xh_name
) == 0)
487 return((struct xtrahash
*) 0);
492 * Return the name from the extra host hash table corresponding
493 * to the passed machine id.
501 if ((mid
& 0200) == 0)
505 printf(gettext("Use made of undefined machine id\n"));
508 return(xtab
[m
]->xh_name
);
513 * Return the bit mask of net's that the given extra host machine
521 if ((mid
& 0200) == 0)
525 printf(gettext("Use made of undefined machine id\n"));
528 return(xtab
[m
]->xh_attnet
);
533 * Take a network name and optimize it. This gloriously messy
534 * operation takes place as follows: the name with machine names
535 * in it is tokenized by mapping each machine name into a single
536 * character machine id (netlook). The separator characters (network
537 * metacharacters) are left intact. The last component of the network
538 * name is stripped off and assumed to be the destination user name --
539 * it does not participate in the optimization. As an example, the
540 * name "res!vax!res!uvax!bill" becomes, tokenized,
541 * "r!x!r!v!" and "bill" A low level routine, optim1, fixes up the
542 * network part (eg, "r!x!r!v!"), then we convert back to network
543 * machine names and tack the user name on the end.
545 * The result of this is copied into the parameter "name"
549 optim(char net
[], char name
[])
551 char netcomp
[BUFSIZ
], netstr
[STSIZ
], xfstr
[STSIZ
];
552 register char *cp
, *cp2
;
555 if (debug
) fprintf(stderr
, "optim(%s, %s) called\n", net
, name
);
560 * Rip off next path component into netcomp
563 while (*cp
&& !any(*cp
, metanet
))
567 * If we hit null byte, then we just scanned
568 * the destination user name. Go off and optimize
573 if ((c
= netlook(netcomp
, *cp
)) == 0) {
574 printf(gettext("No host named \"%s\"\n"), netcomp
);
576 nstrcpy(name
, BUFSIZ
, net
);
579 stradd(name
, BUFSIZ
, c
);
580 stradd(name
, BUFSIZ
, *cp
++);
582 * If multiple network separators given,
583 * throw away the extras.
585 while (any(*cp
, metanet
))
588 if (strlen(netcomp
) == 0) {
589 printf(gettext("net name syntax\n"));
592 if (debug
) fprintf(stderr
, "optim1(%s,%s) called\n", netstr
, xfstr
);
593 optim1(netstr
, xfstr
);
594 if (debug
) fprintf(stderr
, "optim1(%s,%s) returns\n", netstr
, xfstr
);
597 * Convert back to machine names.
603 if ((cp2
= netname(*cp
++)) == NOSTR
) {
604 printf(gettext("Made up bad net name\n"));
605 printf(gettext("Machine code %c (0%o)\n"), cp
[-1],
607 printf(gettext("Sorry.\n"));
610 nstrcat(name
, BUFSIZ
, cp2
);
611 stradd(name
, BUFSIZ
, *cp
++);
613 nstrcat(name
, BUFSIZ
, netcomp
);
614 if (debug
) fprintf(stderr
, "optim returns %s in name\n", name
);
618 * Take a string of network machine id's and separators and
619 * optimize them. We process these by pulling off maximal
620 * leading strings of the same type, passing these to the appropriate
621 * optimizer and concatenating the results.
625 optim1(char netstr
[], char name
[])
627 char path
[STSIZ
], rpath
[STSIZ
];
628 register char *cp
, *cp2
;
635 * If the address ultimately points back to us,
636 * just return a null network path.
638 if ((int)strlen(cp
) > 1 && cp
[strlen(cp
) - 2] == LOCAL
)
645 while (*cp
&& tp
== ntype(cp
[1])) {
646 stradd(path
, sizeof (path
), *cp
++);
649 switch (netkind(tp
)) {
651 nstrcpy(rpath
, sizeof (rpath
), path
);
655 optimimp(path
, rpath
);
659 optimex(path
, rpath
);
662 for (cp2
= rpath
; *cp2
!= 0; cp2
++) {
663 stradd(name
, BUFSIZ
, *cp2
);
664 stradd(name
, BUFSIZ
, nc
);
673 * Return the network of the separator --
675 * BN for Bell labs net (e.g. UUCP, NOT Berknet)
676 * SN for Schmidt net (Berknet)
677 * 0 if we don't know.
680 ntype(register int nc
)
682 register struct ntypetab
*np
;
684 for (np
= ntypetab
; np
->nt_char
!= 0; np
++)
685 if (np
->nt_char
== nc
)
686 return(np
->nt_bcode
);
692 * Return the kind of routing used for the particular net
693 * EXPLICIT means explicitly routed
694 * IMPLICIT means implicitly routed
699 netkind(register int nt
)
701 register struct nkindtab
*np
;
703 for (np
= nkindtab
; np
->nk_type
!= 0; np
++)
704 if (np
->nk_type
== nt
)
710 * Do name optimization for an explicitly routed network (eg uucp).
714 optimex(char net
[], char name
[])
716 register char *cp
, *rp
;
719 nstrcpy(name
, STSIZ
, net
);
723 if (cp
[strlen(cp
)-1] == LOCAL
) {
727 for (cp
= name
; *cp
; cp
++) {
729 rp
= strrchr(cp
+1, m
);
737 * Do name optimization for implicitly routed network (eg, arpanet).
741 optimimp(char net
[], char name
[])
749 m
= cp
[strlen(cp
) - 1];
760 * Perform global optimization on the given network path.
761 * The trick here is to look ahead to see if there are any loops
762 * in the path and remove them. The interpretation of loops is
763 * more strict here than in optimex since both the machine and net
770 register char *cp
, *cp2
;
775 if (((int)strlen(cp
) % 2) != 0) {
776 printf(gettext("Strange arg to optiboth\n"));
780 cp2
= rpair(cp
+2, *cp
);
788 * Find the rightmost instance of the given (machine, type) pair.
792 rpair(char str
[], int mach
)
794 register char *cp
, *last
;
807 * Change the network separators in the given network path
808 * to the preferred network transmission means.
814 register char *cp
, n
;
818 for (cp
= name
; *cp
; cp
+= 2) {
819 n
= best(state
, *cp
);
827 * Return the best network separator for the given machine pair.
831 best(int src
, int dest
)
833 register int dtype
, stype
;
834 register struct netorder
*np
;
836 stype
= nettype(src
);
837 dtype
= nettype(dest
);
839 if (stype
== 0 || dtype
== 0) {
840 printf(gettext("ERROR: unknown internal machine id\n"));
843 if ((stype
& dtype
) == 0)
846 while ((np
->no_stat
& stype
& dtype
) == 0)
854 * Code to twist around arpa net names.
857 #define WORD 257 /* Token for a string */
859 static char netbuf
[256];
863 * Reverse all of the arpa net addresses in the given name to
864 * be of the form "host @ user" instead of "user @ host"
865 * This function is its own inverse.
876 if (strcmp(str
, netbuf
) == 0)
878 return(savestr(netbuf
));
882 * Parse (by recursive descent) network names, using the following grammar:
891 * string of characters.
908 nstrcat(netbuf
, sizeof (netbuf
), cp
);
915 stradd(netbuf
, sizeof (netbuf
), '@');
916 nstrcat(netbuf
, sizeof (netbuf
), cp
);
922 nstrcat(netbuf
, sizeof (netbuf
), cp
);
923 stradd(netbuf
, sizeof (netbuf
), t
);
929 * Scanner for network names.
932 static char *charp
; /* Current input pointer */
933 static int nexttok
; /* Salted away next token */
936 * Initialize the network name scanner.
942 static char lexbuf
[BUFSIZ
];
945 if (strlen(str
) >= sizeof lexbuf
- 1)
948 nstrcpy(lexbuf
, sizeof (lexbuf
), str
);
954 * Scan and return a single token.
955 * yylval is set to point to a scanned string.
961 register char *cp
, *dotp
;
970 while (*cp
&& isspace(*cp
))
974 if (any(*cp
, metanet
)) {
979 while (*cp
&& !any(*cp
, metanet
) && !any(*cp
, " \t"))
981 if (any(*cp
, metanet
))
994 * Add a single character onto a string. Here dstsize is the size of the
999 stradd(register char *dst
, int dstsize
, register int c
)
1001 while (*dst
!= '\0') {