xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 68468)
122717Sdist /*
242833Sbostic  * Copyright (c) 1983 Eric P. Allman
363589Sbostic  * Copyright (c) 1988, 1993
463589Sbostic  *	The Regents of the University of California.  All rights reserved.
533731Sbostic  *
642833Sbostic  * %sccs.include.redist.c%
733731Sbostic  */
822717Sdist 
922717Sdist #ifndef lint
10*68468Seric static char sccsid[] = "@(#)util.c	8.53 (Berkeley) 02/28/95";
1133731Sbostic #endif /* not lint */
1222717Sdist 
1358332Seric # include "sendmail.h"
14298Seric # include <sysexits.h>
1557135Seric /*
16298Seric **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17298Seric **
18298Seric **	Runs through a string and strips off unquoted quote
19298Seric **	characters and quote bits.  This is done in place.
20298Seric **
21298Seric **	Parameters:
22298Seric **		s -- the string to strip.
23298Seric **
24298Seric **	Returns:
25298Seric **		none.
26298Seric **
27298Seric **	Side Effects:
28298Seric **		none.
29298Seric **
30298Seric **	Called By:
31298Seric **		deliver
32298Seric */
33298Seric 
3454983Seric stripquotes(s)
35298Seric 	char *s;
36298Seric {
37298Seric 	register char *p;
38298Seric 	register char *q;
39298Seric 	register char c;
40298Seric 
414101Seric 	if (s == NULL)
424101Seric 		return;
434101Seric 
4454983Seric 	p = q = s;
4554983Seric 	do
46298Seric 	{
4754983Seric 		c = *p++;
4854983Seric 		if (c == '\\')
4954983Seric 			c = *p++;
5054983Seric 		else if (c == '"')
5154983Seric 			continue;
5254983Seric 		*q++ = c;
5354983Seric 	} while (c != '\0');
54298Seric }
55298Seric /*
56298Seric **  XALLOC -- Allocate memory and bitch wildly on failure.
57298Seric **
58298Seric **	THIS IS A CLUDGE.  This should be made to give a proper
59298Seric **	error -- but after all, what can we do?
60298Seric **
61298Seric **	Parameters:
62298Seric **		sz -- size of area to allocate.
63298Seric **
64298Seric **	Returns:
65298Seric **		pointer to data region.
66298Seric **
67298Seric **	Side Effects:
68298Seric **		Memory is allocated.
69298Seric */
70298Seric 
71298Seric char *
72298Seric xalloc(sz)
737007Seric 	register int sz;
74298Seric {
75298Seric 	register char *p;
76298Seric 
7766747Seric 	/* some systems can't handle size zero mallocs */
7866747Seric 	if (sz <= 0)
7966747Seric 		sz = 1;
8066747Seric 
8123121Seric 	p = malloc((unsigned) sz);
82298Seric 	if (p == NULL)
83298Seric 	{
84298Seric 		syserr("Out of memory!!");
8510685Seric 		abort();
8610685Seric 		/* exit(EX_UNAVAILABLE); */
87298Seric 	}
88298Seric 	return (p);
89298Seric }
90298Seric /*
913151Seric **  COPYPLIST -- copy list of pointers.
923151Seric **
933151Seric **	This routine is the equivalent of newstr for lists of
943151Seric **	pointers.
953151Seric **
963151Seric **	Parameters:
973151Seric **		list -- list of pointers to copy.
983151Seric **			Must be NULL terminated.
993151Seric **		copycont -- if TRUE, copy the contents of the vector
1003151Seric **			(which must be a string) also.
1013151Seric **
1023151Seric **	Returns:
1033151Seric **		a copy of 'list'.
1043151Seric **
1053151Seric **	Side Effects:
1063151Seric **		none.
1073151Seric */
1083151Seric 
1093151Seric char **
1103151Seric copyplist(list, copycont)
1113151Seric 	char **list;
1123151Seric 	bool copycont;
1133151Seric {
1143151Seric 	register char **vp;
1153151Seric 	register char **newvp;
1163151Seric 
1173151Seric 	for (vp = list; *vp != NULL; vp++)
1183151Seric 		continue;
1193151Seric 
1203151Seric 	vp++;
1213151Seric 
12216897Seric 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
12316897Seric 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
1243151Seric 
1253151Seric 	if (copycont)
1263151Seric 	{
1273151Seric 		for (vp = newvp; *vp != NULL; vp++)
1283151Seric 			*vp = newstr(*vp);
1293151Seric 	}
1303151Seric 
1313151Seric 	return (newvp);
1323151Seric }
1333151Seric /*
13458170Seric **  COPYQUEUE -- copy address queue.
13558170Seric **
13658170Seric **	This routine is the equivalent of newstr for address queues
13758170Seric **	addresses marked with QDONTSEND aren't copied
13858170Seric **
13958170Seric **	Parameters:
14058170Seric **		addr -- list of address structures to copy.
14158170Seric **
14258170Seric **	Returns:
14358170Seric **		a copy of 'addr'.
14458170Seric **
14558170Seric **	Side Effects:
14658170Seric **		none.
14758170Seric */
14858170Seric 
14958170Seric ADDRESS *
15058170Seric copyqueue(addr)
15158170Seric 	ADDRESS *addr;
15258170Seric {
15358170Seric 	register ADDRESS *newaddr;
15458170Seric 	ADDRESS *ret;
15558170Seric 	register ADDRESS **tail = &ret;
15658170Seric 
15758170Seric 	while (addr != NULL)
15858170Seric 	{
15958170Seric 		if (!bitset(QDONTSEND, addr->q_flags))
16058170Seric 		{
16158170Seric 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
16258170Seric 			STRUCTCOPY(*addr, *newaddr);
16358170Seric 			*tail = newaddr;
16458170Seric 			tail = &newaddr->q_next;
16558170Seric 		}
16658170Seric 		addr = addr->q_next;
16758170Seric 	}
16858170Seric 	*tail = NULL;
16958170Seric 
17058170Seric 	return ret;
17158170Seric }
17258170Seric /*
1733151Seric **  PRINTAV -- print argument vector.
1743151Seric **
1753151Seric **	Parameters:
1763151Seric **		av -- argument vector.
1773151Seric **
1783151Seric **	Returns:
1793151Seric **		none.
1803151Seric **
1813151Seric **	Side Effects:
1823151Seric **		prints av.
1833151Seric */
1843151Seric 
1853151Seric printav(av)
1863151Seric 	register char **av;
1873151Seric {
1883151Seric 	while (*av != NULL)
1893151Seric 	{
1908063Seric 		if (tTd(0, 44))
1918063Seric 			printf("\n\t%08x=", *av);
1928063Seric 		else
19323105Seric 			(void) putchar(' ');
1943151Seric 		xputs(*av++);
1953151Seric 	}
19623105Seric 	(void) putchar('\n');
1973151Seric }
1983151Seric /*
1993151Seric **  LOWER -- turn letter into lower case.
2003151Seric **
2013151Seric **	Parameters:
2023151Seric **		c -- character to turn into lower case.
2033151Seric **
2043151Seric **	Returns:
2053151Seric **		c, in lower case.
2063151Seric **
2073151Seric **	Side Effects:
2083151Seric **		none.
2093151Seric */
2103151Seric 
2113151Seric char
2123151Seric lower(c)
2133151Seric 	register char c;
2143151Seric {
21558050Seric 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
2163151Seric }
2173151Seric /*
2183151Seric **  XPUTS -- put string doing control escapes.
2193151Seric **
2203151Seric **	Parameters:
2213151Seric **		s -- string to put.
2223151Seric **
2233151Seric **	Returns:
2243151Seric **		none.
2253151Seric **
2263151Seric **	Side Effects:
2273151Seric **		output to stdout
2283151Seric */
2293151Seric 
2303151Seric xputs(s)
2313151Seric 	register char *s;
2323151Seric {
23358050Seric 	register int c;
23451781Seric 	register struct metamac *mp;
23551781Seric 	extern struct metamac MetaMacros[];
2363151Seric 
2378055Seric 	if (s == NULL)
2388055Seric 	{
2398055Seric 		printf("<null>");
2408055Seric 		return;
2418055Seric 	}
24258050Seric 	while ((c = (*s++ & 0377)) != '\0')
2433151Seric 	{
2443151Seric 		if (!isascii(c))
2453151Seric 		{
246*68468Seric 			if (c == MATCHREPL)
24758050Seric 			{
24858050Seric 				putchar('$');
24958050Seric 				continue;
25058050Seric 			}
251*68468Seric 			if (c == MACROEXPAND)
252*68468Seric 			{
253*68468Seric 				putchar('$');
254*68468Seric 				if (bitset(0200, *s))
255*68468Seric 					printf("{%s}", macname(*s++ & 0377));
256*68468Seric 				continue;
257*68468Seric 			}
25858050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
25958050Seric 			{
26058050Seric 				if ((mp->metaval & 0377) == c)
26158050Seric 				{
26258050Seric 					printf("$%c", mp->metaname);
26358050Seric 					break;
26458050Seric 				}
26558050Seric 			}
26658050Seric 			if (mp->metaname != '\0')
26758050Seric 				continue;
26823105Seric 			(void) putchar('\\');
2693151Seric 			c &= 0177;
2703151Seric 		}
27157589Seric 		if (isprint(c))
2723151Seric 		{
27357589Seric 			putchar(c);
27457589Seric 			continue;
27557589Seric 		}
27652050Seric 
27757589Seric 		/* wasn't a meta-macro -- find another way to print it */
27857589Seric 		switch (c)
27957589Seric 		{
28057589Seric 		  case '\0':
28157589Seric 			continue;
28252050Seric 
28357589Seric 		  case '\n':
28457589Seric 			c = 'n';
28557589Seric 			break;
28652050Seric 
28757589Seric 		  case '\r':
28857589Seric 			c = 'r';
28957589Seric 			break;
29052637Seric 
29157589Seric 		  case '\t':
29257589Seric 			c = 't';
29357589Seric 			break;
29457589Seric 
29557589Seric 		  default:
29657589Seric 			(void) putchar('^');
29757589Seric 			(void) putchar(c ^ 0100);
29857589Seric 			continue;
2993151Seric 		}
3003151Seric 	}
3014086Seric 	(void) fflush(stdout);
3023151Seric }
3033151Seric /*
3043151Seric **  MAKELOWER -- Translate a line into lower case
3053151Seric **
3063151Seric **	Parameters:
3073151Seric **		p -- the string to translate.  If NULL, return is
3083151Seric **			immediate.
3093151Seric **
3103151Seric **	Returns:
3113151Seric **		none.
3123151Seric **
3133151Seric **	Side Effects:
3143151Seric **		String pointed to by p is translated to lower case.
3153151Seric **
3163151Seric **	Called By:
3173151Seric **		parse
3183151Seric */
3193151Seric 
320*68468Seric void
3213151Seric makelower(p)
3223151Seric 	register char *p;
3233151Seric {
3243151Seric 	register char c;
3253151Seric 
3263151Seric 	if (p == NULL)
3273151Seric 		return;
3283151Seric 	for (; (c = *p) != '\0'; p++)
3293151Seric 		if (isascii(c) && isupper(c))
33033724Sbostic 			*p = tolower(c);
3313151Seric }
3324059Seric /*
3335196Seric **  BUILDFNAME -- build full name from gecos style entry.
3344375Seric **
3355196Seric **	This routine interprets the strange entry that would appear
3365196Seric **	in the GECOS field of the password file.
3375196Seric **
3384375Seric **	Parameters:
3395196Seric **		p -- name to build.
3405196Seric **		login -- the login name of this user (for &).
3415196Seric **		buf -- place to put the result.
3424375Seric **
3434375Seric **	Returns:
34465006Seric **		none.
3454375Seric **
3464375Seric **	Side Effects:
3474375Seric **		none.
3484375Seric */
3494375Seric 
35054984Seric buildfname(gecos, login, buf)
35165006Seric 	register char *gecos;
35265006Seric 	char *login;
35365006Seric 	char *buf;
3544375Seric {
35565006Seric 	register char *p;
35665006Seric 	register char *bp = buf;
35765006Seric 	int l;
3584375Seric 
35965006Seric 	if (*gecos == '*')
36065006Seric 		gecos++;
36165006Seric 
36265006Seric 	/* find length of final string */
36365006Seric 	l = 0;
36465006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
36554984Seric 	{
36665006Seric 		if (*p == '&')
36765006Seric 			l += strlen(login);
36865006Seric 		else
36965006Seric 			l++;
37054984Seric 	}
37165006Seric 
37265006Seric 	/* now fill in buf */
37365006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3744375Seric 	{
37565006Seric 		if (*p == '&')
3764375Seric 		{
37765006Seric 			(void) strcpy(bp, login);
37865006Seric 			*bp = toupper(*bp);
37965006Seric 			while (*bp != '\0')
38065006Seric 				bp++;
3814375Seric 		}
38265006Seric 		else
38365006Seric 			*bp++ = *p;
3844375Seric 	}
3854375Seric 	*bp = '\0';
3864375Seric }
3874375Seric /*
3884538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
3894538Seric **
3904538Seric **	Parameters:
3914538Seric **		fn -- filename to check.
39264083Seric **		uid -- user id to compare against.
39364083Seric **		gid -- group id to compare against.
39464083Seric **		uname -- user name to compare against (used for group
39564083Seric **			sets).
39664944Seric **		flags -- modifiers:
39765064Seric **			SFF_MUSTOWN -- "uid" must own this file.
39865064Seric **			SFF_NOSLINK -- file cannot be a symbolic link.
3994538Seric **		mode -- mode bits that must match.
4004538Seric **
4014538Seric **	Returns:
40258247Seric **		0 if fn exists, is owned by uid, and matches mode.
40358247Seric **		An errno otherwise.  The actual errno is cleared.
4044538Seric **
4054538Seric **	Side Effects:
4064538Seric **		none.
4074538Seric */
4084538Seric 
40964083Seric #include <grp.h>
41064083Seric 
41163581Seric #ifndef S_IXOTH
41263581Seric # define S_IXOTH	(S_IEXEC >> 6)
41363581Seric #endif
41463581Seric 
41564083Seric #ifndef S_IXGRP
41664083Seric # define S_IXGRP	(S_IEXEC >> 3)
41764083Seric #endif
41864083Seric 
41963753Seric #ifndef S_IXUSR
42063753Seric # define S_IXUSR	(S_IEXEC)
42163753Seric #endif
42263753Seric 
42358247Seric int
42464944Seric safefile(fn, uid, gid, uname, flags, mode)
4254538Seric 	char *fn;
42655372Seric 	uid_t uid;
42764083Seric 	gid_t gid;
42864083Seric 	char *uname;
42964944Seric 	int flags;
4304538Seric 	int mode;
4314538Seric {
43263581Seric 	register char *p;
43364083Seric 	register struct group *gr = NULL;
4344538Seric 	struct stat stbuf;
4354538Seric 
43663581Seric 	if (tTd(54, 4))
43764944Seric 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
43864944Seric 			fn, uid, gid, flags, mode);
43963581Seric 	errno = 0;
44063581Seric 
441*68468Seric 	if (!bitset(SFF_NOPATHCHECK, flags) ||
442*68468Seric 	    (uid == 0 && !bitset(SFF_ROOTOK, flags)))
44363581Seric 	{
444*68468Seric 		/* check the path to the file for acceptability */
445*68468Seric 		for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
44665225Seric 		{
447*68468Seric 			*p = '\0';
448*68468Seric 			if (stat(fn, &stbuf) < 0)
449*68468Seric 				break;
450*68468Seric 			if (uid == 0 && !bitset(SFF_ROOTOK, flags))
451*68468Seric 			{
452*68468Seric 				if (bitset(S_IXOTH, stbuf.st_mode))
453*68468Seric 					continue;
454*68468Seric 				break;
455*68468Seric 			}
456*68468Seric 			if (stbuf.st_uid == uid &&
457*68468Seric 			    bitset(S_IXUSR, stbuf.st_mode))
45865225Seric 				continue;
459*68468Seric 			if (stbuf.st_gid == gid &&
460*68468Seric 			    bitset(S_IXGRP, stbuf.st_mode))
461*68468Seric 				continue;
462*68468Seric #ifndef NO_GROUP_SET
463*68468Seric 			if (uname != NULL &&
464*68468Seric 			    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
465*68468Seric 			     (gr = getgrgid(stbuf.st_gid)) != NULL))
466*68468Seric 			{
467*68468Seric 				register char **gp;
468*68468Seric 
469*68468Seric 				for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
470*68468Seric 					if (strcmp(*gp, uname) == 0)
471*68468Seric 						break;
472*68468Seric 				if (gp != NULL && *gp != NULL &&
473*68468Seric 				    bitset(S_IXGRP, stbuf.st_mode))
474*68468Seric 					continue;
475*68468Seric 			}
476*68468Seric #endif
477*68468Seric 			if (!bitset(S_IXOTH, stbuf.st_mode))
478*68468Seric 				break;
47968465Seric 		}
480*68468Seric 		if (p != NULL)
48163581Seric 		{
482*68468Seric 			int ret = errno;
48363581Seric 
484*68468Seric 			if (ret == 0)
485*68468Seric 				ret = EACCES;
486*68468Seric 			if (tTd(54, 4))
487*68468Seric 				printf("\t[dir %s] %s\n", fn, errstring(ret));
488*68468Seric 			*p = '/';
489*68468Seric 			return ret;
49063581Seric 		}
49163581Seric 	}
49263581Seric 
49364944Seric #ifdef HASLSTAT
49465064Seric 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
49565064Seric 					: stat(fn, &stbuf)) < 0)
49664944Seric #else
49758247Seric 	if (stat(fn, &stbuf) < 0)
49864944Seric #endif
49958247Seric 	{
50058247Seric 		int ret = errno;
50158247Seric 
50263581Seric 		if (tTd(54, 4))
50364083Seric 			printf("\t%s\n", errstring(ret));
50463581Seric 
50558247Seric 		errno = 0;
50658247Seric 		return ret;
50758247Seric 	}
50864944Seric 
50964944Seric #ifdef S_ISLNK
51065064Seric 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
51164944Seric 	{
51264944Seric 		if (tTd(54, 4))
51365123Seric 			printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
51464944Seric 		return EPERM;
51564944Seric 	}
51664944Seric #endif
51764944Seric 
51865139Seric 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
51963581Seric 		mode >>= 6;
52064084Seric 	else if (stbuf.st_uid != uid)
52164084Seric 	{
52264084Seric 		mode >>= 3;
52364084Seric 		if (stbuf.st_gid == gid)
52464084Seric 			;
52564084Seric #ifndef NO_GROUP_SET
52664084Seric 		else if (uname != NULL &&
52764084Seric 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
52864084Seric 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
52964084Seric 		{
53064084Seric 			register char **gp;
53164084Seric 
53264084Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
53364084Seric 				if (strcmp(*gp, uname) == 0)
53464084Seric 					break;
53564084Seric 			if (*gp == NULL)
53664084Seric 				mode >>= 3;
53764084Seric 		}
53864084Seric #endif
53964084Seric 		else
54064084Seric 			mode >>= 3;
54164084Seric 	}
54263581Seric 	if (tTd(54, 4))
54364084Seric 		printf("\t[uid %d, stat %o, mode %o] ",
54464084Seric 			stbuf.st_uid, stbuf.st_mode, mode);
54564944Seric 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
54665064Seric 	     !bitset(SFF_MUSTOWN, flags)) &&
54763581Seric 	    (stbuf.st_mode & mode) == mode)
54863581Seric 	{
54963581Seric 		if (tTd(54, 4))
55064083Seric 			printf("\tOK\n");
55158247Seric 		return 0;
55263581Seric 	}
55363581Seric 	if (tTd(54, 4))
55464083Seric 		printf("\tEACCES\n");
55563581Seric 	return EACCES;
5564538Seric }
5574538Seric /*
5584557Seric **  FIXCRLF -- fix <CR><LF> in line.
5594557Seric **
5604557Seric **	Looks for the <CR><LF> combination and turns it into the
5614557Seric **	UNIX canonical <NL> character.  It only takes one line,
5624557Seric **	i.e., it is assumed that the first <NL> found is the end
5634557Seric **	of the line.
5644557Seric **
5654557Seric **	Parameters:
5664557Seric **		line -- the line to fix.
5674557Seric **		stripnl -- if true, strip the newline also.
5684557Seric **
5694557Seric **	Returns:
5704557Seric **		none.
5714557Seric **
5724557Seric **	Side Effects:
5734557Seric **		line is changed in place.
5744557Seric */
5754557Seric 
5764557Seric fixcrlf(line, stripnl)
5774557Seric 	char *line;
5784557Seric 	bool stripnl;
5794557Seric {
5804557Seric 	register char *p;
5814557Seric 
58256795Seric 	p = strchr(line, '\n');
5834557Seric 	if (p == NULL)
5844557Seric 		return;
58536291Sbostic 	if (p > line && p[-1] == '\r')
5864557Seric 		p--;
5874557Seric 	if (!stripnl)
5884557Seric 		*p++ = '\n';
5894557Seric 	*p = '\0';
5904557Seric }
5914557Seric /*
5926890Seric **  DFOPEN -- determined file open
5936890Seric **
5946890Seric **	This routine has the semantics of fopen, except that it will
5956890Seric **	keep trying a few times to make this happen.  The idea is that
5966890Seric **	on very loaded systems, we may run out of resources (inodes,
5976890Seric **	whatever), so this tries to get around it.
5986890Seric */
5996890Seric 
60063753Seric #ifndef O_ACCMODE
60163753Seric # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
60263753Seric #endif
60363753Seric 
60459745Seric struct omodes
60559745Seric {
60659745Seric 	int	mask;
60759745Seric 	int	mode;
60859745Seric 	char	*farg;
60959745Seric } OpenModes[] =
61059745Seric {
61159745Seric 	O_ACCMODE,		O_RDONLY,		"r",
61259745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
61359745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
61459745Seric 	O_TRUNC,		0,			"w+",
61559745Seric 	O_APPEND,		O_APPEND,		"a+",
61659745Seric 	0,			0,			"r+",
61759745Seric };
61859745Seric 
6196890Seric FILE *
62059745Seric dfopen(filename, omode, cmode)
6216890Seric 	char *filename;
62259745Seric 	int omode;
62359745Seric 	int cmode;
6246890Seric {
6256890Seric 	register int tries;
62659745Seric 	int fd;
62759745Seric 	register struct omodes *om;
62859431Seric 	struct stat st;
6296890Seric 
63059745Seric 	for (om = OpenModes; om->mask != 0; om++)
63159745Seric 		if ((omode & om->mask) == om->mode)
63259745Seric 			break;
63359745Seric 
6346890Seric 	for (tries = 0; tries < 10; tries++)
6356890Seric 	{
63625618Seric 		sleep((unsigned) (10 * tries));
6376890Seric 		errno = 0;
63859745Seric 		fd = open(filename, omode, cmode);
63959745Seric 		if (fd >= 0)
6406890Seric 			break;
64166017Seric 		switch (errno)
64266017Seric 		{
64366017Seric 		  case ENFILE:		/* system file table full */
64466017Seric 		  case EINTR:		/* interrupted syscall */
64566017Seric #ifdef ETXTBSY
64666017Seric 		  case ETXTBSY:		/* Apollo: net file locked */
64766017Seric #endif
64866017Seric 			continue;
64966017Seric 		}
65066017Seric 		break;
6516890Seric 	}
65259745Seric 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
65356328Seric 	{
65456328Seric 		int locktype;
65556328Seric 
65656328Seric 		/* lock the file to avoid accidental conflicts */
65759745Seric 		if ((omode & O_ACCMODE) != O_RDONLY)
65856328Seric 			locktype = LOCK_EX;
65956328Seric 		else
66056328Seric 			locktype = LOCK_SH;
66164335Seric 		(void) lockfile(fd, filename, NULL, locktype);
66256328Seric 		errno = 0;
66356328Seric 	}
66463787Seric 	if (fd < 0)
66563787Seric 		return NULL;
66663787Seric 	else
66763787Seric 		return fdopen(fd, om->farg);
6686890Seric }
6697124Seric /*
6707124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
6717124Seric **
6727753Seric **	This routine always guarantees outputing a newline (or CRLF,
6737753Seric **	as appropriate) at the end of the string.
6747753Seric **
6757124Seric **	Parameters:
6767124Seric **		l -- line to put.
67765870Seric **		mci -- the mailer connection information.
6787124Seric **
6797124Seric **	Returns:
6807124Seric **		none
6817124Seric **
6827124Seric **	Side Effects:
6837124Seric **		output of l to fp.
6847124Seric */
6857124Seric 
68665870Seric putline(l, mci)
6877753Seric 	register char *l;
68865870Seric 	register MCI *mci;
6897124Seric {
6907124Seric 	register char *p;
69147157Sbostic 	register char svchar;
69266004Seric 	int slop = 0;
6937124Seric 
69411275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
69565870Seric 	if (bitset(MCIF_7BIT, mci->mci_flags))
69611275Seric 	{
69761707Seric 		for (p = l; (svchar = *p) != '\0'; ++p)
69861707Seric 			if (bitset(0200, svchar))
69947157Sbostic 				*p = svchar &~ 0200;
70011275Seric 	}
70111275Seric 
7027753Seric 	do
7037124Seric 	{
7047753Seric 		/* find the end of the line */
70556795Seric 		p = strchr(l, '\n');
7067753Seric 		if (p == NULL)
7077753Seric 			p = &l[strlen(l)];
7087124Seric 
70963753Seric 		if (TrafficLogFile != NULL)
71063753Seric 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
71163753Seric 
7127753Seric 		/* check for line overflow */
71365870Seric 		while (mci->mci_mailer->m_linelimit > 0 &&
71466004Seric 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
7157753Seric 		{
71666004Seric 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
7177124Seric 
7187753Seric 			svchar = *q;
7197753Seric 			*q = '\0';
72066004Seric 			if (l[0] == '.' && slop == 0 &&
72166004Seric 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
72263753Seric 			{
72365870Seric 				(void) putc('.', mci->mci_out);
72463753Seric 				if (TrafficLogFile != NULL)
72563753Seric 					(void) putc('.', TrafficLogFile);
72663753Seric 			}
72765870Seric 			fputs(l, mci->mci_out);
72865870Seric 			(void) putc('!', mci->mci_out);
72965870Seric 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
73066004Seric 			(void) putc(' ', mci->mci_out);
73163753Seric 			if (TrafficLogFile != NULL)
73266004Seric 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
73363753Seric 					l, getpid());
7347753Seric 			*q = svchar;
7357753Seric 			l = q;
73666004Seric 			slop = 1;
7377753Seric 		}
7387124Seric 
7397753Seric 		/* output last part */
74066004Seric 		if (l[0] == '.' && slop == 0 &&
74166004Seric 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
74263753Seric 		{
74365870Seric 			(void) putc('.', mci->mci_out);
74463753Seric 			if (TrafficLogFile != NULL)
74563753Seric 				(void) putc('.', TrafficLogFile);
74663753Seric 		}
74763753Seric 		if (TrafficLogFile != NULL)
74863753Seric 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
74947157Sbostic 		for ( ; l < p; ++l)
75065870Seric 			(void) putc(*l, mci->mci_out);
75165870Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
7527753Seric 		if (*l == '\n')
75347157Sbostic 			++l;
7547753Seric 	} while (l[0] != '\0');
7557124Seric }
7567676Seric /*
7577676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
7587676Seric **
7597676Seric **	Parameters:
7607676Seric **		f -- name of file to unlink.
7617676Seric **
7627676Seric **	Returns:
7637676Seric **		none.
7647676Seric **
7657676Seric **	Side Effects:
7667676Seric **		f is unlinked.
7677676Seric */
7687676Seric 
7697676Seric xunlink(f)
7707676Seric 	char *f;
7717676Seric {
7727676Seric 	register int i;
7737676Seric 
7747676Seric # ifdef LOG
77558020Seric 	if (LogLevel > 98)
77658020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
77756795Seric # endif /* LOG */
7787676Seric 
7797676Seric 	i = unlink(f);
7807676Seric # ifdef LOG
78158020Seric 	if (i < 0 && LogLevel > 97)
7827942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
78356795Seric # endif /* LOG */
7847676Seric }
7857685Seric /*
78658680Seric **  XFCLOSE -- close a file, doing logging as appropriate.
78758680Seric **
78858680Seric **	Parameters:
78958680Seric **		fp -- file pointer for the file to close
79058680Seric **		a, b -- miscellaneous crud to print for debugging
79158680Seric **
79258680Seric **	Returns:
79358680Seric **		none.
79458680Seric **
79558680Seric **	Side Effects:
79658680Seric **		fp is closed.
79758680Seric */
79858680Seric 
79958680Seric xfclose(fp, a, b)
80058680Seric 	FILE *fp;
80158680Seric 	char *a, *b;
80258680Seric {
80358796Seric 	if (tTd(53, 99))
80458680Seric 		printf("xfclose(%x) %s %s\n", fp, a, b);
80564401Seric #ifdef XDEBUG
80664401Seric 	if (fileno(fp) == 1)
80764401Seric 		syserr("xfclose(%s %s): fd = 1", a, b);
80864401Seric #endif
80958796Seric 	if (fclose(fp) < 0 && tTd(53, 99))
81058680Seric 		printf("xfclose FAILURE: %s\n", errstring(errno));
81158680Seric }
81258680Seric /*
81314885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
8147685Seric **
8157685Seric **	Parameters:
8167685Seric **		buf -- place to put the input line.
8177685Seric **		siz -- size of buf.
8187685Seric **		fp -- file to read from.
81957384Seric **		timeout -- the timeout before error occurs.
82061093Seric **		during -- what we are trying to read (for error messages).
8217685Seric **
8227685Seric **	Returns:
82315533Seric **		NULL on error (including timeout).  This will also leave
82415533Seric **			buf containing a null string.
8257685Seric **		buf otherwise.
8267685Seric **
8277685Seric **	Side Effects:
8287685Seric **		none.
8297685Seric */
8307685Seric 
83114885Seric static jmp_buf	CtxReadTimeout;
832*68468Seric static void	readtimeout();
8337685Seric 
8347685Seric char *
83561093Seric sfgets(buf, siz, fp, timeout, during)
8367685Seric 	char *buf;
8377685Seric 	int siz;
8387685Seric 	FILE *fp;
83957384Seric 	time_t timeout;
84061093Seric 	char *during;
8417685Seric {
8427942Seric 	register EVENT *ev = NULL;
8437685Seric 	register char *p;
8447685Seric 
84566332Seric 	if (fp == NULL)
84666332Seric 	{
84766332Seric 		buf[0] = '\0';
84866332Seric 		return NULL;
84966332Seric 	}
85066332Seric 
85114885Seric 	/* set the timeout */
85257384Seric 	if (timeout != 0)
85314885Seric 	{
85414885Seric 		if (setjmp(CtxReadTimeout) != 0)
85514885Seric 		{
85636233Skarels # ifdef LOG
85736230Skarels 			syslog(LOG_NOTICE,
85861093Seric 			    "timeout waiting for input from %s during %s\n",
85961093Seric 			    CurHostName? CurHostName: "local", during);
86036233Skarels # endif
86136230Skarels 			errno = 0;
86261093Seric 			usrerr("451 timeout waiting for input during %s",
86361093Seric 				during);
86419037Seric 			buf[0] = '\0';
86563753Seric #ifdef XDEBUG
86663753Seric 			checkfd012(during);
86763753Seric #endif
86814885Seric 			return (NULL);
86914885Seric 		}
870*68468Seric 		ev = setevent(timeout, readtimeout, 0);
87114885Seric 	}
87214885Seric 
87314885Seric 	/* try to read */
87415533Seric 	p = NULL;
87565190Seric 	while (!feof(fp) && !ferror(fp))
8767942Seric 	{
8777942Seric 		errno = 0;
8787942Seric 		p = fgets(buf, siz, fp);
87965190Seric 		if (p != NULL || errno != EINTR)
88065186Seric 			break;
88165190Seric 		clearerr(fp);
88215533Seric 	}
88314885Seric 
88414885Seric 	/* clear the event if it has not sprung */
885*68468Seric 	clrevent(ev);
88614885Seric 
88714885Seric 	/* clean up the books and exit */
8888055Seric 	LineNumber++;
88915533Seric 	if (p == NULL)
89016880Seric 	{
89115533Seric 		buf[0] = '\0';
89263753Seric 		if (TrafficLogFile != NULL)
89363753Seric 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
89416880Seric 		return (NULL);
89516880Seric 	}
89663753Seric 	if (TrafficLogFile != NULL)
89763753Seric 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
898*68468Seric 	if (SevenBitInput)
899*68468Seric 	{
90052106Seric 		for (p = buf; *p != '\0'; p++)
90152106Seric 			*p &= ~0200;
90267546Seric 	}
903*68468Seric 	else if (!HasEightBits)
90467546Seric 	{
905*68468Seric 		for (p = buf; *p != '\0'; p++)
906*68468Seric 		{
907*68468Seric 			if (bitset(0200, *p))
908*68468Seric 			{
909*68468Seric 				HasEightBits = TRUE;
910*68468Seric 				break;
911*68468Seric 			}
912*68468Seric 		}
91367546Seric 	}
914*68468Seric 	return (buf);
9157685Seric }
9167685Seric 
917*68468Seric static void
91866765Seric readtimeout(timeout)
91966765Seric 	time_t timeout;
9207685Seric {
921*68468Seric 	longjmp(CtxReadTimeout, 1);
9227685Seric }
9237786Seric /*
9247786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
9257786Seric **
9267786Seric **	Parameters:
9277786Seric **		buf -- place to put result.
9287786Seric **		n -- bytes available.
9297786Seric **		f -- file to read from.
9307786Seric **
9317786Seric **	Returns:
93257135Seric **		input line(s) on success, NULL on error or EOF.
93357135Seric **		This will normally be buf -- unless the line is too
93457135Seric **			long, when it will be xalloc()ed.
9357786Seric **
9367786Seric **	Side Effects:
9377786Seric **		buf gets lines from f, with continuation lines (lines
9387786Seric **		with leading white space) appended.  CRLF's are mapped
9397786Seric **		into single newlines.  Any trailing NL is stripped.
9407786Seric */
9417786Seric 
9427786Seric char *
9437786Seric fgetfolded(buf, n, f)
9447786Seric 	char *buf;
9457786Seric 	register int n;
9467786Seric 	FILE *f;
9477786Seric {
9487786Seric 	register char *p = buf;
94957135Seric 	char *bp = buf;
9507786Seric 	register int i;
9517786Seric 
9527786Seric 	n--;
95317350Seric 	while ((i = getc(f)) != EOF)
9547786Seric 	{
95517350Seric 		if (i == '\r')
95617350Seric 		{
95717350Seric 			i = getc(f);
95817350Seric 			if (i != '\n')
95917350Seric 			{
96017350Seric 				if (i != EOF)
96123105Seric 					(void) ungetc(i, f);
96217350Seric 				i = '\r';
96317350Seric 			}
96417350Seric 		}
96557135Seric 		if (--n <= 0)
96657135Seric 		{
96757135Seric 			/* allocate new space */
96857135Seric 			char *nbp;
96957135Seric 			int nn;
97057135Seric 
97157135Seric 			nn = (p - bp);
97257232Seric 			if (nn < MEMCHUNKSIZE)
97357135Seric 				nn *= 2;
97457135Seric 			else
97557232Seric 				nn += MEMCHUNKSIZE;
97657135Seric 			nbp = xalloc(nn);
97757135Seric 			bcopy(bp, nbp, p - bp);
97857135Seric 			p = &nbp[p - bp];
97957135Seric 			if (bp != buf)
98057135Seric 				free(bp);
98157135Seric 			bp = nbp;
98257135Seric 			n = nn - (p - bp);
98357135Seric 		}
98457135Seric 		*p++ = i;
98517350Seric 		if (i == '\n')
98617350Seric 		{
98717350Seric 			LineNumber++;
98817350Seric 			i = getc(f);
98917350Seric 			if (i != EOF)
99023105Seric 				(void) ungetc(i, f);
99117350Seric 			if (i != ' ' && i != '\t')
99252647Seric 				break;
99317350Seric 		}
9947786Seric 	}
99557135Seric 	if (p == bp)
99652647Seric 		return (NULL);
99752647Seric 	*--p = '\0';
99857135Seric 	return (bp);
9997786Seric }
10007860Seric /*
10017886Seric **  CURTIME -- return current time.
10027886Seric **
10037886Seric **	Parameters:
10047886Seric **		none.
10057886Seric **
10067886Seric **	Returns:
10077886Seric **		the current time.
10087886Seric **
10097886Seric **	Side Effects:
10107886Seric **		none.
10117886Seric */
10127886Seric 
10137886Seric time_t
10147886Seric curtime()
10157886Seric {
10167886Seric 	auto time_t t;
10177886Seric 
10187886Seric 	(void) time(&t);
10197886Seric 	return (t);
10207886Seric }
10218264Seric /*
10228264Seric **  ATOBOOL -- convert a string representation to boolean.
10238264Seric **
10248264Seric **	Defaults to "TRUE"
10258264Seric **
10268264Seric **	Parameters:
10278264Seric **		s -- string to convert.  Takes "tTyY" as true,
10288264Seric **			others as false.
10298264Seric **
10308264Seric **	Returns:
10318264Seric **		A boolean representation of the string.
10328264Seric **
10338264Seric **	Side Effects:
10348264Seric **		none.
10358264Seric */
10368264Seric 
10378264Seric bool
10388264Seric atobool(s)
10398264Seric 	register char *s;
10408264Seric {
104163833Seric 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
10428264Seric 		return (TRUE);
10438264Seric 	return (FALSE);
10448264Seric }
10459048Seric /*
10469048Seric **  ATOOCT -- convert a string representation to octal.
10479048Seric **
10489048Seric **	Parameters:
10499048Seric **		s -- string to convert.
10509048Seric **
10519048Seric **	Returns:
10529048Seric **		An integer representing the string interpreted as an
10539048Seric **		octal number.
10549048Seric **
10559048Seric **	Side Effects:
10569048Seric **		none.
10579048Seric */
10589048Seric 
10599048Seric atooct(s)
10609048Seric 	register char *s;
10619048Seric {
10629048Seric 	register int i = 0;
10639048Seric 
10649048Seric 	while (*s >= '0' && *s <= '7')
10659048Seric 		i = (i << 3) | (*s++ - '0');
10669048Seric 	return (i);
10679048Seric }
10689376Seric /*
10699376Seric **  WAITFOR -- wait for a particular process id.
10709376Seric **
10719376Seric **	Parameters:
10729376Seric **		pid -- process id to wait for.
10739376Seric **
10749376Seric **	Returns:
10759376Seric **		status of pid.
10769376Seric **		-1 if pid never shows up.
10779376Seric **
10789376Seric **	Side Effects:
10799376Seric **		none.
10809376Seric */
10819376Seric 
108264562Seric int
10839376Seric waitfor(pid)
10849376Seric 	int pid;
10859376Seric {
108664562Seric #ifdef WAITUNION
108764562Seric 	union wait st;
108864562Seric #else
10899376Seric 	auto int st;
109064562Seric #endif
10919376Seric 	int i;
10929376Seric 
10939376Seric 	do
10949376Seric 	{
10959376Seric 		errno = 0;
10969376Seric 		i = wait(&st);
10979376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
10989376Seric 	if (i < 0)
109964562Seric 		return -1;
110064562Seric #ifdef WAITUNION
110164562Seric 	return st.w_status;
110264562Seric #else
110364562Seric 	return st;
110464562Seric #endif
11059376Seric }
11069376Seric /*
110710685Seric **  BITINTERSECT -- tell if two bitmaps intersect
110810685Seric **
110910685Seric **	Parameters:
111010685Seric **		a, b -- the bitmaps in question
111110685Seric **
111210685Seric **	Returns:
111310685Seric **		TRUE if they have a non-null intersection
111410685Seric **		FALSE otherwise
111510685Seric **
111610685Seric **	Side Effects:
111710685Seric **		none.
111810685Seric */
111910685Seric 
112010685Seric bool
112110685Seric bitintersect(a, b)
112210685Seric 	BITMAP a;
112310685Seric 	BITMAP b;
112410685Seric {
112510685Seric 	int i;
112610685Seric 
112710685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
112810685Seric 		if ((a[i] & b[i]) != 0)
112910685Seric 			return (TRUE);
113010685Seric 	return (FALSE);
113110685Seric }
113210685Seric /*
113310685Seric **  BITZEROP -- tell if a bitmap is all zero
113410685Seric **
113510685Seric **	Parameters:
113610685Seric **		map -- the bit map to check
113710685Seric **
113810685Seric **	Returns:
113910685Seric **		TRUE if map is all zero.
114010685Seric **		FALSE if there are any bits set in map.
114110685Seric **
114210685Seric **	Side Effects:
114310685Seric **		none.
114410685Seric */
114510685Seric 
114610685Seric bool
114710685Seric bitzerop(map)
114810685Seric 	BITMAP map;
114910685Seric {
115010685Seric 	int i;
115110685Seric 
115210685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
115310685Seric 		if (map[i] != 0)
115410685Seric 			return (FALSE);
115510685Seric 	return (TRUE);
115610685Seric }
115758247Seric /*
115858318Seric **  STRCONTAINEDIN -- tell if one string is contained in another
115958318Seric **
116058318Seric **	Parameters:
116158318Seric **		a -- possible substring.
116258318Seric **		b -- possible superstring.
116358318Seric **
116458318Seric **	Returns:
116558318Seric **		TRUE if a is contained in b.
116658318Seric **		FALSE otherwise.
116758318Seric */
116858318Seric 
116958318Seric bool
117058318Seric strcontainedin(a, b)
117158318Seric 	register char *a;
117258318Seric 	register char *b;
117358318Seric {
117465012Seric 	int la;
117565012Seric 	int lb;
117665012Seric 	int c;
117758318Seric 
117865012Seric 	la = strlen(a);
117965012Seric 	lb = strlen(b);
118065012Seric 	c = *a;
118165012Seric 	if (isascii(c) && isupper(c))
118265012Seric 		c = tolower(c);
118365012Seric 	for (; lb-- >= la; b++)
118458318Seric 	{
118565012Seric 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
118665012Seric 			continue;
118765012Seric 		if (strncasecmp(a, b, la) == 0)
118858318Seric 			return TRUE;
118958318Seric 	}
119065012Seric 	return FALSE;
119158318Seric }
119263753Seric /*
119363753Seric **  CHECKFD012 -- check low numbered file descriptors
119463753Seric **
119563753Seric **	File descriptors 0, 1, and 2 should be open at all times.
119663753Seric **	This routine verifies that, and fixes it if not true.
119763753Seric **
119863753Seric **	Parameters:
119963753Seric **		where -- a tag printed if the assertion failed
120063753Seric **
120163753Seric **	Returns:
120263753Seric **		none
120363753Seric */
120463753Seric 
120563753Seric checkfd012(where)
120663753Seric 	char *where;
120763753Seric {
120863753Seric #ifdef XDEBUG
120963753Seric 	register int i;
121063753Seric 	struct stat stbuf;
121163753Seric 
121263753Seric 	for (i = 0; i < 3; i++)
121363753Seric 	{
121464735Seric 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
121563753Seric 		{
121663753Seric 			/* oops.... */
121763753Seric 			int fd;
121863753Seric 
121963753Seric 			syserr("%s: fd %d not open", where, i);
122063753Seric 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
122163753Seric 			if (fd != i)
122263753Seric 			{
122363753Seric 				(void) dup2(fd, i);
122463753Seric 				(void) close(fd);
122563753Seric 			}
122663753Seric 		}
122763753Seric 	}
122863937Seric #endif /* XDEBUG */
122963753Seric }
123064725Seric /*
123164725Seric **  PRINTOPENFDS -- print the open file descriptors (for debugging)
123264725Seric **
123364725Seric **	Parameters:
123464725Seric **		logit -- if set, send output to syslog; otherwise
123564725Seric **			print for debugging.
123664725Seric **
123764725Seric **	Returns:
123864725Seric **		none.
123964725Seric */
124064725Seric 
124164725Seric #include <netdb.h>
124264725Seric #include <arpa/inet.h>
124364725Seric 
124464725Seric printopenfds(logit)
124564725Seric 	bool logit;
124664725Seric {
124764725Seric 	register int fd;
124864743Seric 	extern int DtableSize;
124964743Seric 
125064743Seric 	for (fd = 0; fd < DtableSize; fd++)
125164743Seric 		dumpfd(fd, FALSE, logit);
125264743Seric }
125364743Seric /*
125464743Seric **  DUMPFD -- dump a file descriptor
125564743Seric **
125664743Seric **	Parameters:
125764743Seric **		fd -- the file descriptor to dump.
125864743Seric **		printclosed -- if set, print a notification even if
125964743Seric **			it is closed; otherwise print nothing.
126064743Seric **		logit -- if set, send output to syslog instead of stdout.
126164743Seric */
126264743Seric 
126364743Seric dumpfd(fd, printclosed, logit)
126464743Seric 	int fd;
126564743Seric 	bool printclosed;
126664743Seric 	bool logit;
126764743Seric {
126864725Seric 	register struct hostent *hp;
126964725Seric 	register char *p;
127066747Seric 	char *fmtstr;
127164743Seric 	struct sockaddr_in sin;
127264743Seric 	auto int slen;
127364743Seric 	struct stat st;
127464725Seric 	char buf[200];
127564725Seric 
127664743Seric 	p = buf;
127764743Seric 	sprintf(p, "%3d: ", fd);
127864743Seric 	p += strlen(p);
127964743Seric 
128064743Seric 	if (fstat(fd, &st) < 0)
128164725Seric 	{
128264743Seric 		if (printclosed || errno != EBADF)
128364743Seric 		{
128464743Seric 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
128564743Seric 			goto printit;
128664743Seric 		}
128764743Seric 		return;
128864743Seric 	}
128964725Seric 
129064743Seric 	slen = fcntl(fd, F_GETFL, NULL);
129164743Seric 	if (slen != -1)
129264743Seric 	{
129364743Seric 		sprintf(p, "fl=0x%x, ", slen);
129464743Seric 		p += strlen(p);
129564743Seric 	}
129664725Seric 
129764743Seric 	sprintf(p, "mode=%o: ", st.st_mode);
129864743Seric 	p += strlen(p);
129964743Seric 	switch (st.st_mode & S_IFMT)
130064743Seric 	{
130164807Seric #ifdef S_IFSOCK
130264743Seric 	  case S_IFSOCK:
130364743Seric 		sprintf(p, "SOCK ");
130464725Seric 		p += strlen(p);
130564743Seric 		slen = sizeof sin;
130664743Seric 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
130764743Seric 			sprintf(p, "(badsock)");
130864743Seric 		else
130964725Seric 		{
1310*68468Seric 			hp = gethostbyaddr((char *) &sin.sin_addr,
1311*68468Seric 					   INADDRSZ, AF_INET);
131264743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
131364743Seric 						   : hp->h_name, ntohs(sin.sin_port));
131464743Seric 		}
131564743Seric 		p += strlen(p);
131664743Seric 		sprintf(p, "->");
131764743Seric 		p += strlen(p);
131864743Seric 		slen = sizeof sin;
131964743Seric 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
132064743Seric 			sprintf(p, "(badsock)");
132164743Seric 		else
132264743Seric 		{
1323*68468Seric 			hp = gethostbyaddr((char *) &sin.sin_addr,
1324*68468Seric 					   INADDRSZ, AF_INET);
132564743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
132664743Seric 						   : hp->h_name, ntohs(sin.sin_port));
132764743Seric 		}
132864743Seric 		break;
132964807Seric #endif
133064725Seric 
133164743Seric 	  case S_IFCHR:
133264743Seric 		sprintf(p, "CHR: ");
133364743Seric 		p += strlen(p);
133464743Seric 		goto defprint;
133564725Seric 
133664743Seric 	  case S_IFBLK:
133764743Seric 		sprintf(p, "BLK: ");
133864743Seric 		p += strlen(p);
133964743Seric 		goto defprint;
134064725Seric 
134166252Seric #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
134265378Seric 	  case S_IFIFO:
134365378Seric 		sprintf(p, "FIFO: ");
134465378Seric 		p += strlen(p);
134565378Seric 		goto defprint;
134665378Seric #endif
134765378Seric 
134865378Seric #ifdef S_IFDIR
134965378Seric 	  case S_IFDIR:
135065378Seric 		sprintf(p, "DIR: ");
135165378Seric 		p += strlen(p);
135265378Seric 		goto defprint;
135365378Seric #endif
135465378Seric 
135565378Seric #ifdef S_IFLNK
135665378Seric 	  case S_IFLNK:
135765378Seric 		sprintf(p, "LNK: ");
135865378Seric 		p += strlen(p);
135965378Seric 		goto defprint;
136065378Seric #endif
136165378Seric 
136264743Seric 	  default:
136364725Seric defprint:
136466785Seric 		if (sizeof st.st_size > sizeof (long))
136566751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
136666747Seric 		else
136766751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
136866747Seric 		sprintf(p, fmtstr,
136964743Seric 			major(st.st_dev), minor(st.st_dev), st.st_ino,
137064743Seric 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
137164743Seric 		break;
137264743Seric 	}
137364725Seric 
137464743Seric printit:
137566748Seric #ifdef LOG
137664743Seric 	if (logit)
137765006Seric 		syslog(LOG_DEBUG, "%s", buf);
137864743Seric 	else
137966748Seric #endif
138064743Seric 		printf("%s\n", buf);
138164725Seric }
138265015Seric /*
138365015Seric **  SHORTENSTRING -- return short version of a string
138465015Seric **
138565015Seric **	If the string is already short, just return it.  If it is too
138665015Seric **	long, return the head and tail of the string.
138765015Seric **
138865015Seric **	Parameters:
138965015Seric **		s -- the string to shorten.
139065015Seric **		m -- the max length of the string.
139165015Seric **
139265015Seric **	Returns:
139365015Seric **		Either s or a short version of s.
139465015Seric */
139565015Seric 
139665015Seric #ifndef MAXSHORTSTR
139765055Seric # define MAXSHORTSTR	203
139865015Seric #endif
139965015Seric 
140065015Seric char *
140165015Seric shortenstring(s, m)
140265015Seric 	register char *s;
140365015Seric 	int m;
140465015Seric {
140565015Seric 	int l;
140665015Seric 	static char buf[MAXSHORTSTR + 1];
140765015Seric 
140865015Seric 	l = strlen(s);
140965015Seric 	if (l < m)
141065015Seric 		return s;
141165015Seric 	if (m > MAXSHORTSTR)
141265015Seric 		m = MAXSHORTSTR;
141365015Seric 	else if (m < 10)
141465015Seric 	{
141565015Seric 		if (m < 5)
141665015Seric 		{
141765015Seric 			strncpy(buf, s, m);
141865015Seric 			buf[m] = '\0';
141965015Seric 			return buf;
142065015Seric 		}
142165015Seric 		strncpy(buf, s, m - 3);
142265015Seric 		strcpy(buf + m - 3, "...");
142365015Seric 		return buf;
142465015Seric 	}
142565015Seric 	m = (m - 3) / 2;
142665015Seric 	strncpy(buf, s, m);
142765015Seric 	strcpy(buf + m, "...");
142865015Seric 	strcpy(buf + m + 3, s + l - m);
142965015Seric 	return buf;
143065015Seric }
143167848Seric /*
1432*68468Seric **  GET_COLUMN  -- look up a Column in a line buffer
1433*68468Seric **
1434*68468Seric **	Parameters:
1435*68468Seric **		line -- the raw text line to search.
1436*68468Seric **		col -- the column number to fetch.
1437*68468Seric **		delim -- the delimiter between columns.  If null,
1438*68468Seric **			use white space.
1439*68468Seric **		buf -- the output buffer.
1440*68468Seric **
1441*68468Seric **	Returns:
1442*68468Seric **		buf if successful.
1443*68468Seric **		NULL otherwise.
1444*68468Seric */
1445*68468Seric 
1446*68468Seric char *
1447*68468Seric get_column(line, col, delim, buf)
1448*68468Seric 	char line[];
1449*68468Seric 	int col;
1450*68468Seric 	char delim;
1451*68468Seric 	char buf[];
1452*68468Seric {
1453*68468Seric 	char *p;
1454*68468Seric 	char *begin, *end;
1455*68468Seric 	int i;
1456*68468Seric 	char delimbuf[3];
1457*68468Seric 
1458*68468Seric 	if (delim == '\0')
1459*68468Seric 		strcpy(delimbuf, "\t ");
1460*68468Seric 	else
1461*68468Seric 	{
1462*68468Seric 		delimbuf[0] = delim;
1463*68468Seric 		delimbuf[1] = '\0';
1464*68468Seric 	}
1465*68468Seric 
1466*68468Seric 	p = line;
1467*68468Seric 	if (*p == '\0')
1468*68468Seric 		return NULL;			/* line empty */
1469*68468Seric 	if (*p == delim && col == 0)
1470*68468Seric 		return NULL;			/* first column empty */
1471*68468Seric 
1472*68468Seric 	begin = line;
1473*68468Seric 
1474*68468Seric 	if (col == 0 && delim == '\0')
1475*68468Seric 	{
1476*68468Seric 		while (*begin && isspace(*begin))
1477*68468Seric 			begin++;
1478*68468Seric 	}
1479*68468Seric 
1480*68468Seric 	for (i = 0; i < col; i++)
1481*68468Seric 	{
1482*68468Seric 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1483*68468Seric 			return NULL;		/* no such column */
1484*68468Seric 		begin++;
1485*68468Seric 		if (delim == '\0')
1486*68468Seric 		{
1487*68468Seric 			while (*begin && isspace(*begin))
1488*68468Seric 				begin++;
1489*68468Seric 		}
1490*68468Seric 	}
1491*68468Seric 
1492*68468Seric 	end = strpbrk(begin, delimbuf);
1493*68468Seric 	if (end == NULL)
1494*68468Seric 	{
1495*68468Seric 		strcpy(buf, begin);
1496*68468Seric 	}
1497*68468Seric 	else
1498*68468Seric 	{
1499*68468Seric 		strncpy(buf, begin, end - begin);
1500*68468Seric 		buf[end - begin] = '\0';
1501*68468Seric 	}
1502*68468Seric 	return buf;
1503*68468Seric }
1504*68468Seric /*
150568267Seric **  CLEANSTRCPY -- copy string keeping out bogus characters
150668267Seric **
150768267Seric **	Parameters:
150868267Seric **		t -- "to" string.
150968267Seric **		f -- "from" string.
151068267Seric **		l -- length of space available in "to" string.
151168267Seric **
151268267Seric **	Returns:
151368267Seric **		none.
151468267Seric */
151568267Seric 
151668267Seric void
151768267Seric cleanstrcpy(t, f, l)
151868267Seric 	register char *t;
151968267Seric 	register char *f;
152068267Seric 	int l;
152168267Seric {
152268281Seric #ifdef LOG
152368281Seric 	/* check for newlines and log if necessary */
152468457Seric 	(void) denlstring(f, TRUE);
152568281Seric #endif
152668281Seric 
152768267Seric 	l--;
152868267Seric 	while (l > 0 && *f != '\0')
152968267Seric 	{
153068267Seric 		if (isascii(*f) &&
153168267Seric 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
153268267Seric 		{
153368267Seric 			l--;
153468267Seric 			*t++ = *f;
153568267Seric 		}
153668267Seric 		f++;
153768267Seric 	}
153868267Seric 	*t = '\0';
153968267Seric }
154068267Seric /*
154168267Seric **  DENLSTRING -- convert newlines in a string to spaces
154268267Seric **
154368267Seric **	Parameters:
154468267Seric **		s -- the input string
154568457Seric **		logattacks -- if set, log attempted attacks.
154668267Seric **
154768267Seric **	Returns:
154868267Seric **		A pointer to a version of the string with newlines
154968267Seric **		mapped to spaces.  This should be copied.
155068267Seric */
155168267Seric 
155268267Seric char *
155368457Seric denlstring(s, logattacks)
155468267Seric 	char *s;
155568465Seric 	int logattacks;
155668267Seric {
155768267Seric 	register char *p;
155868267Seric 	int l;
155968267Seric 	static char *bp = NULL;
156068267Seric 	static int bl = 0;
156168267Seric 
156268267Seric 	if (strchr(s, '\n') == NULL)
156368267Seric 		return s;
156468267Seric 
156568267Seric 	l = strlen(s) + 1;
156668267Seric 	if (bl < l)
156768267Seric 	{
156868267Seric 		/* allocate more space */
156968267Seric 		if (bp != NULL)
157068267Seric 			free(bp);
157168267Seric 		bp = xalloc(l);
157268267Seric 		bl = l;
157368267Seric 	}
157468267Seric 	strcpy(bp, s);
157568267Seric 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
157668267Seric 		*p++ = ' ';
157768281Seric 
1578*68468Seric /*
157968281Seric #ifdef LOG
158068457Seric 	if (logattacks)
158168457Seric 	{
158268457Seric 		syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
158368457Seric 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
158468457Seric 			shortenstring(bp, 80));
158568457Seric 	}
158668281Seric #endif
1587*68468Seric */
158868281Seric 
158968267Seric 	return bp;
159068267Seric }
1591