xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 68478)
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*68478Seric static char sccsid[] = "@(#)util.c	8.39.1.5 (Berkeley) 03/05/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*68478Seric 			if (c == MATCHREPL || c == MACROEXPAND)
24758050Seric 			{
24858050Seric 				putchar('$');
24958050Seric 				continue;
25058050Seric 			}
25158050Seric 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
25258050Seric 			{
25358050Seric 				if ((mp->metaval & 0377) == c)
25458050Seric 				{
25558050Seric 					printf("$%c", mp->metaname);
25658050Seric 					break;
25758050Seric 				}
25858050Seric 			}
25958050Seric 			if (mp->metaname != '\0')
26058050Seric 				continue;
26123105Seric 			(void) putchar('\\');
2623151Seric 			c &= 0177;
2633151Seric 		}
26457589Seric 		if (isprint(c))
2653151Seric 		{
26657589Seric 			putchar(c);
26757589Seric 			continue;
26857589Seric 		}
26952050Seric 
27057589Seric 		/* wasn't a meta-macro -- find another way to print it */
27157589Seric 		switch (c)
27257589Seric 		{
27357589Seric 		  case '\0':
27457589Seric 			continue;
27552050Seric 
27657589Seric 		  case '\n':
27757589Seric 			c = 'n';
27857589Seric 			break;
27952050Seric 
28057589Seric 		  case '\r':
28157589Seric 			c = 'r';
28257589Seric 			break;
28352637Seric 
28457589Seric 		  case '\t':
28557589Seric 			c = 't';
28657589Seric 			break;
28757589Seric 
28857589Seric 		  default:
28957589Seric 			(void) putchar('^');
29057589Seric 			(void) putchar(c ^ 0100);
29157589Seric 			continue;
2923151Seric 		}
2933151Seric 	}
2944086Seric 	(void) fflush(stdout);
2953151Seric }
2963151Seric /*
2973151Seric **  MAKELOWER -- Translate a line into lower case
2983151Seric **
2993151Seric **	Parameters:
3003151Seric **		p -- the string to translate.  If NULL, return is
3013151Seric **			immediate.
3023151Seric **
3033151Seric **	Returns:
3043151Seric **		none.
3053151Seric **
3063151Seric **	Side Effects:
3073151Seric **		String pointed to by p is translated to lower case.
3083151Seric **
3093151Seric **	Called By:
3103151Seric **		parse
3113151Seric */
3123151Seric 
3133151Seric makelower(p)
3143151Seric 	register char *p;
3153151Seric {
3163151Seric 	register char c;
3173151Seric 
3183151Seric 	if (p == NULL)
3193151Seric 		return;
3203151Seric 	for (; (c = *p) != '\0'; p++)
3213151Seric 		if (isascii(c) && isupper(c))
32233724Sbostic 			*p = tolower(c);
3233151Seric }
3244059Seric /*
3255196Seric **  BUILDFNAME -- build full name from gecos style entry.
3264375Seric **
3275196Seric **	This routine interprets the strange entry that would appear
3285196Seric **	in the GECOS field of the password file.
3295196Seric **
3304375Seric **	Parameters:
3315196Seric **		p -- name to build.
3325196Seric **		login -- the login name of this user (for &).
3335196Seric **		buf -- place to put the result.
3344375Seric **
3354375Seric **	Returns:
33665006Seric **		none.
3374375Seric **
3384375Seric **	Side Effects:
3394375Seric **		none.
3404375Seric */
3414375Seric 
34254984Seric buildfname(gecos, login, buf)
34365006Seric 	register char *gecos;
34465006Seric 	char *login;
34565006Seric 	char *buf;
3464375Seric {
34765006Seric 	register char *p;
34865006Seric 	register char *bp = buf;
34965006Seric 	int l;
3504375Seric 
35165006Seric 	if (*gecos == '*')
35265006Seric 		gecos++;
35365006Seric 
35465006Seric 	/* find length of final string */
35565006Seric 	l = 0;
35665006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
35754984Seric 	{
35865006Seric 		if (*p == '&')
35965006Seric 			l += strlen(login);
36065006Seric 		else
36165006Seric 			l++;
36254984Seric 	}
36365006Seric 
36465006Seric 	/* now fill in buf */
36565006Seric 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
3664375Seric 	{
36765006Seric 		if (*p == '&')
3684375Seric 		{
36965006Seric 			(void) strcpy(bp, login);
37065006Seric 			*bp = toupper(*bp);
37165006Seric 			while (*bp != '\0')
37265006Seric 				bp++;
3734375Seric 		}
37465006Seric 		else
37565006Seric 			*bp++ = *p;
3764375Seric 	}
3774375Seric 	*bp = '\0';
3784375Seric }
3794375Seric /*
3804538Seric **  SAFEFILE -- return true if a file exists and is safe for a user.
3814538Seric **
3824538Seric **	Parameters:
3834538Seric **		fn -- filename to check.
38464083Seric **		uid -- user id to compare against.
38564083Seric **		gid -- group id to compare against.
38664083Seric **		uname -- user name to compare against (used for group
38764083Seric **			sets).
38864944Seric **		flags -- modifiers:
38965064Seric **			SFF_MUSTOWN -- "uid" must own this file.
39065064Seric **			SFF_NOSLINK -- file cannot be a symbolic link.
3914538Seric **		mode -- mode bits that must match.
3924538Seric **
3934538Seric **	Returns:
39458247Seric **		0 if fn exists, is owned by uid, and matches mode.
39558247Seric **		An errno otherwise.  The actual errno is cleared.
3964538Seric **
3974538Seric **	Side Effects:
3984538Seric **		none.
3994538Seric */
4004538Seric 
40164083Seric #include <grp.h>
40264083Seric 
40363581Seric #ifndef S_IXOTH
40463581Seric # define S_IXOTH	(S_IEXEC >> 6)
40563581Seric #endif
40663581Seric 
40764083Seric #ifndef S_IXGRP
40864083Seric # define S_IXGRP	(S_IEXEC >> 3)
40964083Seric #endif
41064083Seric 
41163753Seric #ifndef S_IXUSR
41263753Seric # define S_IXUSR	(S_IEXEC)
41363753Seric #endif
41463753Seric 
41558247Seric int
41664944Seric safefile(fn, uid, gid, uname, flags, mode)
4174538Seric 	char *fn;
41855372Seric 	uid_t uid;
41964083Seric 	gid_t gid;
42064083Seric 	char *uname;
42164944Seric 	int flags;
4224538Seric 	int mode;
4234538Seric {
42463581Seric 	register char *p;
42564083Seric 	register struct group *gr = NULL;
4264538Seric 	struct stat stbuf;
4274538Seric 
42863581Seric 	if (tTd(54, 4))
42964944Seric 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
43064944Seric 			fn, uid, gid, flags, mode);
43163581Seric 	errno = 0;
43263581Seric 
433*68478Seric 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
43463581Seric 	{
435*68478Seric 		*p = '\0';
436*68478Seric 		if (stat(fn, &stbuf) < 0)
437*68478Seric 			break;
438*68478Seric 		if (uid == 0 && !bitset(SFF_ROOTOK, flags))
43965225Seric 		{
440*68478Seric 			if (bitset(S_IXOTH, stbuf.st_mode))
44165225Seric 				continue;
442*68478Seric 			break;
443*68478Seric 		}
444*68478Seric 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
445*68478Seric 			continue;
446*68478Seric 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
447*68478Seric 			continue;
44868468Seric #ifndef NO_GROUP_SET
449*68478Seric 		if (uname != NULL &&
450*68478Seric 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
451*68478Seric 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
45263581Seric 		{
453*68478Seric 			register char **gp;
45463581Seric 
455*68478Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
456*68478Seric 				if (strcmp(*gp, uname) == 0)
457*68478Seric 					break;
458*68478Seric 			if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
459*68478Seric 				continue;
46063581Seric 		}
461*68478Seric #endif
462*68478Seric 		if (!bitset(S_IXOTH, stbuf.st_mode))
463*68478Seric 			break;
46463581Seric 	}
465*68478Seric 	if (p != NULL)
466*68478Seric 	{
467*68478Seric 		int ret = errno;
46863581Seric 
469*68478Seric 		if (ret == 0)
470*68478Seric 			ret = EACCES;
471*68478Seric 		if (tTd(54, 4))
472*68478Seric 			printf("\t[dir %s] %s\n", fn, errstring(ret));
473*68478Seric 		*p = '/';
474*68478Seric 		return ret;
475*68478Seric 	}
476*68478Seric 
47764944Seric #ifdef HASLSTAT
47865064Seric 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
47965064Seric 					: stat(fn, &stbuf)) < 0)
48064944Seric #else
48158247Seric 	if (stat(fn, &stbuf) < 0)
48264944Seric #endif
48358247Seric 	{
48458247Seric 		int ret = errno;
48558247Seric 
48663581Seric 		if (tTd(54, 4))
48764083Seric 			printf("\t%s\n", errstring(ret));
48863581Seric 
48958247Seric 		errno = 0;
49058247Seric 		return ret;
49158247Seric 	}
49264944Seric 
49364944Seric #ifdef S_ISLNK
49465064Seric 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
49564944Seric 	{
49664944Seric 		if (tTd(54, 4))
49765123Seric 			printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
49864944Seric 		return EPERM;
49964944Seric 	}
50064944Seric #endif
50164944Seric 
50265139Seric 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
50363581Seric 		mode >>= 6;
50464084Seric 	else if (stbuf.st_uid != uid)
50564084Seric 	{
50664084Seric 		mode >>= 3;
50764084Seric 		if (stbuf.st_gid == gid)
50864084Seric 			;
50964084Seric #ifndef NO_GROUP_SET
51064084Seric 		else if (uname != NULL &&
51164084Seric 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
51264084Seric 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
51364084Seric 		{
51464084Seric 			register char **gp;
51564084Seric 
51664084Seric 			for (gp = gr->gr_mem; *gp != NULL; gp++)
51764084Seric 				if (strcmp(*gp, uname) == 0)
51864084Seric 					break;
51964084Seric 			if (*gp == NULL)
52064084Seric 				mode >>= 3;
52164084Seric 		}
52264084Seric #endif
52364084Seric 		else
52464084Seric 			mode >>= 3;
52564084Seric 	}
52663581Seric 	if (tTd(54, 4))
52764084Seric 		printf("\t[uid %d, stat %o, mode %o] ",
52864084Seric 			stbuf.st_uid, stbuf.st_mode, mode);
52964944Seric 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
53065064Seric 	     !bitset(SFF_MUSTOWN, flags)) &&
53163581Seric 	    (stbuf.st_mode & mode) == mode)
53263581Seric 	{
53363581Seric 		if (tTd(54, 4))
53464083Seric 			printf("\tOK\n");
53558247Seric 		return 0;
53663581Seric 	}
53763581Seric 	if (tTd(54, 4))
53864083Seric 		printf("\tEACCES\n");
53963581Seric 	return EACCES;
5404538Seric }
5414538Seric /*
5424557Seric **  FIXCRLF -- fix <CR><LF> in line.
5434557Seric **
5444557Seric **	Looks for the <CR><LF> combination and turns it into the
5454557Seric **	UNIX canonical <NL> character.  It only takes one line,
5464557Seric **	i.e., it is assumed that the first <NL> found is the end
5474557Seric **	of the line.
5484557Seric **
5494557Seric **	Parameters:
5504557Seric **		line -- the line to fix.
5514557Seric **		stripnl -- if true, strip the newline also.
5524557Seric **
5534557Seric **	Returns:
5544557Seric **		none.
5554557Seric **
5564557Seric **	Side Effects:
5574557Seric **		line is changed in place.
5584557Seric */
5594557Seric 
5604557Seric fixcrlf(line, stripnl)
5614557Seric 	char *line;
5624557Seric 	bool stripnl;
5634557Seric {
5644557Seric 	register char *p;
5654557Seric 
56656795Seric 	p = strchr(line, '\n');
5674557Seric 	if (p == NULL)
5684557Seric 		return;
56936291Sbostic 	if (p > line && p[-1] == '\r')
5704557Seric 		p--;
5714557Seric 	if (!stripnl)
5724557Seric 		*p++ = '\n';
5734557Seric 	*p = '\0';
5744557Seric }
5754557Seric /*
5766890Seric **  DFOPEN -- determined file open
5776890Seric **
5786890Seric **	This routine has the semantics of fopen, except that it will
5796890Seric **	keep trying a few times to make this happen.  The idea is that
5806890Seric **	on very loaded systems, we may run out of resources (inodes,
5816890Seric **	whatever), so this tries to get around it.
5826890Seric */
5836890Seric 
58463753Seric #ifndef O_ACCMODE
58563753Seric # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
58663753Seric #endif
58763753Seric 
58859745Seric struct omodes
58959745Seric {
59059745Seric 	int	mask;
59159745Seric 	int	mode;
59259745Seric 	char	*farg;
59359745Seric } OpenModes[] =
59459745Seric {
59559745Seric 	O_ACCMODE,		O_RDONLY,		"r",
59659745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
59759745Seric 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
59859745Seric 	O_TRUNC,		0,			"w+",
59959745Seric 	O_APPEND,		O_APPEND,		"a+",
60059745Seric 	0,			0,			"r+",
60159745Seric };
60259745Seric 
6036890Seric FILE *
60459745Seric dfopen(filename, omode, cmode)
6056890Seric 	char *filename;
60659745Seric 	int omode;
60759745Seric 	int cmode;
6086890Seric {
6096890Seric 	register int tries;
61059745Seric 	int fd;
61159745Seric 	register struct omodes *om;
61259431Seric 	struct stat st;
6136890Seric 
61459745Seric 	for (om = OpenModes; om->mask != 0; om++)
61559745Seric 		if ((omode & om->mask) == om->mode)
61659745Seric 			break;
61759745Seric 
6186890Seric 	for (tries = 0; tries < 10; tries++)
6196890Seric 	{
62025618Seric 		sleep((unsigned) (10 * tries));
6216890Seric 		errno = 0;
62259745Seric 		fd = open(filename, omode, cmode);
62359745Seric 		if (fd >= 0)
6246890Seric 			break;
62566017Seric 		switch (errno)
62666017Seric 		{
62766017Seric 		  case ENFILE:		/* system file table full */
62866017Seric 		  case EINTR:		/* interrupted syscall */
62966017Seric #ifdef ETXTBSY
63066017Seric 		  case ETXTBSY:		/* Apollo: net file locked */
63166017Seric #endif
63266017Seric 			continue;
63366017Seric 		}
63466017Seric 		break;
6356890Seric 	}
63659745Seric 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
63756328Seric 	{
63856328Seric 		int locktype;
63956328Seric 
64056328Seric 		/* lock the file to avoid accidental conflicts */
64159745Seric 		if ((omode & O_ACCMODE) != O_RDONLY)
64256328Seric 			locktype = LOCK_EX;
64356328Seric 		else
64456328Seric 			locktype = LOCK_SH;
64564335Seric 		(void) lockfile(fd, filename, NULL, locktype);
64656328Seric 		errno = 0;
64756328Seric 	}
64863787Seric 	if (fd < 0)
64963787Seric 		return NULL;
65063787Seric 	else
65163787Seric 		return fdopen(fd, om->farg);
6526890Seric }
6537124Seric /*
6547124Seric **  PUTLINE -- put a line like fputs obeying SMTP conventions
6557124Seric **
6567753Seric **	This routine always guarantees outputing a newline (or CRLF,
6577753Seric **	as appropriate) at the end of the string.
6587753Seric **
6597124Seric **	Parameters:
6607124Seric **		l -- line to put.
66165870Seric **		mci -- the mailer connection information.
6627124Seric **
6637124Seric **	Returns:
6647124Seric **		none
6657124Seric **
6667124Seric **	Side Effects:
6677124Seric **		output of l to fp.
6687124Seric */
6697124Seric 
67065870Seric putline(l, mci)
6717753Seric 	register char *l;
67265870Seric 	register MCI *mci;
6737124Seric {
6747124Seric 	register char *p;
67547157Sbostic 	register char svchar;
67666004Seric 	int slop = 0;
6777124Seric 
67811275Seric 	/* strip out 0200 bits -- these can look like TELNET protocol */
67965870Seric 	if (bitset(MCIF_7BIT, mci->mci_flags))
68011275Seric 	{
68161707Seric 		for (p = l; (svchar = *p) != '\0'; ++p)
68261707Seric 			if (bitset(0200, svchar))
68347157Sbostic 				*p = svchar &~ 0200;
68411275Seric 	}
68511275Seric 
6867753Seric 	do
6877124Seric 	{
6887753Seric 		/* find the end of the line */
68956795Seric 		p = strchr(l, '\n');
6907753Seric 		if (p == NULL)
6917753Seric 			p = &l[strlen(l)];
6927124Seric 
69363753Seric 		if (TrafficLogFile != NULL)
69463753Seric 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
69563753Seric 
6967753Seric 		/* check for line overflow */
69765870Seric 		while (mci->mci_mailer->m_linelimit > 0 &&
69866004Seric 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
6997753Seric 		{
70066004Seric 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
7017124Seric 
7027753Seric 			svchar = *q;
7037753Seric 			*q = '\0';
70466004Seric 			if (l[0] == '.' && slop == 0 &&
70566004Seric 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
70663753Seric 			{
70765870Seric 				(void) putc('.', mci->mci_out);
70863753Seric 				if (TrafficLogFile != NULL)
70963753Seric 					(void) putc('.', TrafficLogFile);
71063753Seric 			}
71165870Seric 			fputs(l, mci->mci_out);
71265870Seric 			(void) putc('!', mci->mci_out);
71365870Seric 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
71466004Seric 			(void) putc(' ', mci->mci_out);
71563753Seric 			if (TrafficLogFile != NULL)
71666004Seric 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
71763753Seric 					l, getpid());
7187753Seric 			*q = svchar;
7197753Seric 			l = q;
72066004Seric 			slop = 1;
7217753Seric 		}
7227124Seric 
7237753Seric 		/* output last part */
72466004Seric 		if (l[0] == '.' && slop == 0 &&
72566004Seric 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
72663753Seric 		{
72765870Seric 			(void) putc('.', mci->mci_out);
72863753Seric 			if (TrafficLogFile != NULL)
72963753Seric 				(void) putc('.', TrafficLogFile);
73063753Seric 		}
73163753Seric 		if (TrafficLogFile != NULL)
73263753Seric 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
73347157Sbostic 		for ( ; l < p; ++l)
73465870Seric 			(void) putc(*l, mci->mci_out);
73565870Seric 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
7367753Seric 		if (*l == '\n')
73747157Sbostic 			++l;
7387753Seric 	} while (l[0] != '\0');
7397124Seric }
7407676Seric /*
7417676Seric **  XUNLINK -- unlink a file, doing logging as appropriate.
7427676Seric **
7437676Seric **	Parameters:
7447676Seric **		f -- name of file to unlink.
7457676Seric **
7467676Seric **	Returns:
7477676Seric **		none.
7487676Seric **
7497676Seric **	Side Effects:
7507676Seric **		f is unlinked.
7517676Seric */
7527676Seric 
7537676Seric xunlink(f)
7547676Seric 	char *f;
7557676Seric {
7567676Seric 	register int i;
7577676Seric 
7587676Seric # ifdef LOG
75958020Seric 	if (LogLevel > 98)
76058020Seric 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
76156795Seric # endif /* LOG */
7627676Seric 
7637676Seric 	i = unlink(f);
7647676Seric # ifdef LOG
76558020Seric 	if (i < 0 && LogLevel > 97)
7667942Seric 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
76756795Seric # endif /* LOG */
7687676Seric }
7697685Seric /*
77058680Seric **  XFCLOSE -- close a file, doing logging as appropriate.
77158680Seric **
77258680Seric **	Parameters:
77358680Seric **		fp -- file pointer for the file to close
77458680Seric **		a, b -- miscellaneous crud to print for debugging
77558680Seric **
77658680Seric **	Returns:
77758680Seric **		none.
77858680Seric **
77958680Seric **	Side Effects:
78058680Seric **		fp is closed.
78158680Seric */
78258680Seric 
78358680Seric xfclose(fp, a, b)
78458680Seric 	FILE *fp;
78558680Seric 	char *a, *b;
78658680Seric {
78758796Seric 	if (tTd(53, 99))
78858680Seric 		printf("xfclose(%x) %s %s\n", fp, a, b);
78964401Seric #ifdef XDEBUG
79064401Seric 	if (fileno(fp) == 1)
79164401Seric 		syserr("xfclose(%s %s): fd = 1", a, b);
79264401Seric #endif
79358796Seric 	if (fclose(fp) < 0 && tTd(53, 99))
79458680Seric 		printf("xfclose FAILURE: %s\n", errstring(errno));
79558680Seric }
79658680Seric /*
79714885Seric **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
7987685Seric **
7997685Seric **	Parameters:
8007685Seric **		buf -- place to put the input line.
8017685Seric **		siz -- size of buf.
8027685Seric **		fp -- file to read from.
80357384Seric **		timeout -- the timeout before error occurs.
80461093Seric **		during -- what we are trying to read (for error messages).
8057685Seric **
8067685Seric **	Returns:
80715533Seric **		NULL on error (including timeout).  This will also leave
80815533Seric **			buf containing a null string.
8097685Seric **		buf otherwise.
8107685Seric **
8117685Seric **	Side Effects:
8127685Seric **		none.
8137685Seric */
8147685Seric 
81514885Seric static jmp_buf	CtxReadTimeout;
816*68478Seric static int	readtimeout();
817*68478Seric static EVENT	*GlobalTimeout = NULL;
818*68478Seric static bool	EnableTimeout = FALSE;
819*68478Seric static int	ReadProgress;
8207685Seric 
8217685Seric char *
82261093Seric sfgets(buf, siz, fp, timeout, during)
8237685Seric 	char *buf;
8247685Seric 	int siz;
8257685Seric 	FILE *fp;
82657384Seric 	time_t timeout;
82761093Seric 	char *during;
8287685Seric {
8297942Seric 	register EVENT *ev = NULL;
8307685Seric 	register char *p;
8317685Seric 
83266332Seric 	if (fp == NULL)
83366332Seric 	{
83466332Seric 		buf[0] = '\0';
83566332Seric 		return NULL;
83666332Seric 	}
83766332Seric 
83814885Seric 	/* set the timeout */
83957384Seric 	if (timeout != 0)
84014885Seric 	{
84114885Seric 		if (setjmp(CtxReadTimeout) != 0)
84214885Seric 		{
84336233Skarels # ifdef LOG
84436230Skarels 			syslog(LOG_NOTICE,
84561093Seric 			    "timeout waiting for input from %s during %s\n",
84661093Seric 			    CurHostName? CurHostName: "local", during);
84736233Skarels # endif
84836230Skarels 			errno = 0;
84961093Seric 			usrerr("451 timeout waiting for input during %s",
85061093Seric 				during);
85119037Seric 			buf[0] = '\0';
85263753Seric #ifdef XDEBUG
85363753Seric 			checkfd012(during);
85463753Seric #endif
85514885Seric 			return (NULL);
85614885Seric 		}
857*68478Seric 		if (GlobalTimeout == NULL)
858*68478Seric 			ev = setevent(timeout, readtimeout, 0);
859*68478Seric 		else
860*68478Seric 			EnableTimeout = TRUE;
86114885Seric 	}
86214885Seric 
86314885Seric 	/* try to read */
86415533Seric 	p = NULL;
86565190Seric 	while (!feof(fp) && !ferror(fp))
8667942Seric 	{
8677942Seric 		errno = 0;
8687942Seric 		p = fgets(buf, siz, fp);
86965190Seric 		if (p != NULL || errno != EINTR)
87065186Seric 			break;
87165190Seric 		clearerr(fp);
87215533Seric 	}
87314885Seric 
87414885Seric 	/* clear the event if it has not sprung */
875*68478Seric 	if (GlobalTimeout == NULL)
876*68478Seric 		clrevent(ev);
877*68478Seric 	else
878*68478Seric 		EnableTimeout = FALSE;
87914885Seric 
88014885Seric 	/* clean up the books and exit */
8818055Seric 	LineNumber++;
88215533Seric 	if (p == NULL)
88316880Seric 	{
88415533Seric 		buf[0] = '\0';
88563753Seric 		if (TrafficLogFile != NULL)
88663753Seric 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
88716880Seric 		return (NULL);
88816880Seric 	}
88963753Seric 	if (TrafficLogFile != NULL)
89063753Seric 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
891*68478Seric 	if (SevenBit)
89252106Seric 		for (p = buf; *p != '\0'; p++)
89352106Seric 			*p &= ~0200;
894*68478Seric 	return (buf);
895*68478Seric }
896*68478Seric 
897*68478Seric void
898*68478Seric sfgetset(timeout)
899*68478Seric 	time_t timeout;
900*68478Seric {
901*68478Seric 	/* cancel pending timer */
902*68478Seric 	if (GlobalTimeout != NULL)
903*68478Seric 	{
904*68478Seric 		clrevent(GlobalTimeout);
905*68478Seric 		GlobalTimeout = NULL;
90667546Seric 	}
907*68478Seric 
908*68478Seric 	/* schedule fresh one if so requested */
909*68478Seric 	if (timeout != 0)
91067546Seric 	{
911*68478Seric 		ReadProgress = LineNumber;
912*68478Seric 		GlobalTimeout = setevent(timeout, readtimeout, timeout);
91367546Seric 	}
9147685Seric }
9157685Seric 
916*68478Seric static
91766765Seric readtimeout(timeout)
91866765Seric 	time_t timeout;
9197685Seric {
920*68478Seric 	/* terminate if ordinary timeout */
921*68478Seric 	if (GlobalTimeout == NULL)
922*68478Seric 		longjmp(CtxReadTimeout, 1);
923*68478Seric 
924*68478Seric 	/* terminate if no progress was made -- reset state */
925*68478Seric 	if (EnableTimeout && (LineNumber <= ReadProgress))
926*68478Seric 	{
927*68478Seric 		EnableTimeout = FALSE;
928*68478Seric 		GlobalTimeout = NULL;
929*68478Seric 		longjmp(CtxReadTimeout, 2);
930*68478Seric 	}
931*68478Seric 
932*68478Seric 	/* schedule a new timeout */
933*68478Seric 	GlobalTimeout = NULL;
934*68478Seric 	sfgetset(timeout);
9357685Seric }
9367786Seric /*
9377786Seric **  FGETFOLDED -- like fgets, but know about folded lines.
9387786Seric **
9397786Seric **	Parameters:
9407786Seric **		buf -- place to put result.
9417786Seric **		n -- bytes available.
9427786Seric **		f -- file to read from.
9437786Seric **
9447786Seric **	Returns:
94557135Seric **		input line(s) on success, NULL on error or EOF.
94657135Seric **		This will normally be buf -- unless the line is too
94757135Seric **			long, when it will be xalloc()ed.
9487786Seric **
9497786Seric **	Side Effects:
9507786Seric **		buf gets lines from f, with continuation lines (lines
9517786Seric **		with leading white space) appended.  CRLF's are mapped
9527786Seric **		into single newlines.  Any trailing NL is stripped.
9537786Seric */
9547786Seric 
9557786Seric char *
9567786Seric fgetfolded(buf, n, f)
9577786Seric 	char *buf;
9587786Seric 	register int n;
9597786Seric 	FILE *f;
9607786Seric {
9617786Seric 	register char *p = buf;
96257135Seric 	char *bp = buf;
9637786Seric 	register int i;
9647786Seric 
9657786Seric 	n--;
96617350Seric 	while ((i = getc(f)) != EOF)
9677786Seric 	{
96817350Seric 		if (i == '\r')
96917350Seric 		{
97017350Seric 			i = getc(f);
97117350Seric 			if (i != '\n')
97217350Seric 			{
97317350Seric 				if (i != EOF)
97423105Seric 					(void) ungetc(i, f);
97517350Seric 				i = '\r';
97617350Seric 			}
97717350Seric 		}
97857135Seric 		if (--n <= 0)
97957135Seric 		{
98057135Seric 			/* allocate new space */
98157135Seric 			char *nbp;
98257135Seric 			int nn;
98357135Seric 
98457135Seric 			nn = (p - bp);
98557232Seric 			if (nn < MEMCHUNKSIZE)
98657135Seric 				nn *= 2;
98757135Seric 			else
98857232Seric 				nn += MEMCHUNKSIZE;
98957135Seric 			nbp = xalloc(nn);
99057135Seric 			bcopy(bp, nbp, p - bp);
99157135Seric 			p = &nbp[p - bp];
99257135Seric 			if (bp != buf)
99357135Seric 				free(bp);
99457135Seric 			bp = nbp;
99557135Seric 			n = nn - (p - bp);
99657135Seric 		}
99757135Seric 		*p++ = i;
99817350Seric 		if (i == '\n')
99917350Seric 		{
100017350Seric 			LineNumber++;
100117350Seric 			i = getc(f);
100217350Seric 			if (i != EOF)
100323105Seric 				(void) ungetc(i, f);
100417350Seric 			if (i != ' ' && i != '\t')
100552647Seric 				break;
100617350Seric 		}
10077786Seric 	}
100857135Seric 	if (p == bp)
100952647Seric 		return (NULL);
101052647Seric 	*--p = '\0';
101157135Seric 	return (bp);
10127786Seric }
10137860Seric /*
10147886Seric **  CURTIME -- return current time.
10157886Seric **
10167886Seric **	Parameters:
10177886Seric **		none.
10187886Seric **
10197886Seric **	Returns:
10207886Seric **		the current time.
10217886Seric **
10227886Seric **	Side Effects:
10237886Seric **		none.
10247886Seric */
10257886Seric 
10267886Seric time_t
10277886Seric curtime()
10287886Seric {
10297886Seric 	auto time_t t;
10307886Seric 
10317886Seric 	(void) time(&t);
10327886Seric 	return (t);
10337886Seric }
10348264Seric /*
10358264Seric **  ATOBOOL -- convert a string representation to boolean.
10368264Seric **
10378264Seric **	Defaults to "TRUE"
10388264Seric **
10398264Seric **	Parameters:
10408264Seric **		s -- string to convert.  Takes "tTyY" as true,
10418264Seric **			others as false.
10428264Seric **
10438264Seric **	Returns:
10448264Seric **		A boolean representation of the string.
10458264Seric **
10468264Seric **	Side Effects:
10478264Seric **		none.
10488264Seric */
10498264Seric 
10508264Seric bool
10518264Seric atobool(s)
10528264Seric 	register char *s;
10538264Seric {
105463833Seric 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
10558264Seric 		return (TRUE);
10568264Seric 	return (FALSE);
10578264Seric }
10589048Seric /*
10599048Seric **  ATOOCT -- convert a string representation to octal.
10609048Seric **
10619048Seric **	Parameters:
10629048Seric **		s -- string to convert.
10639048Seric **
10649048Seric **	Returns:
10659048Seric **		An integer representing the string interpreted as an
10669048Seric **		octal number.
10679048Seric **
10689048Seric **	Side Effects:
10699048Seric **		none.
10709048Seric */
10719048Seric 
10729048Seric atooct(s)
10739048Seric 	register char *s;
10749048Seric {
10759048Seric 	register int i = 0;
10769048Seric 
10779048Seric 	while (*s >= '0' && *s <= '7')
10789048Seric 		i = (i << 3) | (*s++ - '0');
10799048Seric 	return (i);
10809048Seric }
10819376Seric /*
10829376Seric **  WAITFOR -- wait for a particular process id.
10839376Seric **
10849376Seric **	Parameters:
10859376Seric **		pid -- process id to wait for.
10869376Seric **
10879376Seric **	Returns:
10889376Seric **		status of pid.
10899376Seric **		-1 if pid never shows up.
10909376Seric **
10919376Seric **	Side Effects:
10929376Seric **		none.
10939376Seric */
10949376Seric 
109564562Seric int
10969376Seric waitfor(pid)
10979376Seric 	int pid;
10989376Seric {
109964562Seric #ifdef WAITUNION
110064562Seric 	union wait st;
110164562Seric #else
11029376Seric 	auto int st;
110364562Seric #endif
11049376Seric 	int i;
11059376Seric 
11069376Seric 	do
11079376Seric 	{
11089376Seric 		errno = 0;
11099376Seric 		i = wait(&st);
11109376Seric 	} while ((i >= 0 || errno == EINTR) && i != pid);
11119376Seric 	if (i < 0)
111264562Seric 		return -1;
111364562Seric #ifdef WAITUNION
111464562Seric 	return st.w_status;
111564562Seric #else
111664562Seric 	return st;
111764562Seric #endif
11189376Seric }
11199376Seric /*
112010685Seric **  BITINTERSECT -- tell if two bitmaps intersect
112110685Seric **
112210685Seric **	Parameters:
112310685Seric **		a, b -- the bitmaps in question
112410685Seric **
112510685Seric **	Returns:
112610685Seric **		TRUE if they have a non-null intersection
112710685Seric **		FALSE otherwise
112810685Seric **
112910685Seric **	Side Effects:
113010685Seric **		none.
113110685Seric */
113210685Seric 
113310685Seric bool
113410685Seric bitintersect(a, b)
113510685Seric 	BITMAP a;
113610685Seric 	BITMAP b;
113710685Seric {
113810685Seric 	int i;
113910685Seric 
114010685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
114110685Seric 		if ((a[i] & b[i]) != 0)
114210685Seric 			return (TRUE);
114310685Seric 	return (FALSE);
114410685Seric }
114510685Seric /*
114610685Seric **  BITZEROP -- tell if a bitmap is all zero
114710685Seric **
114810685Seric **	Parameters:
114910685Seric **		map -- the bit map to check
115010685Seric **
115110685Seric **	Returns:
115210685Seric **		TRUE if map is all zero.
115310685Seric **		FALSE if there are any bits set in map.
115410685Seric **
115510685Seric **	Side Effects:
115610685Seric **		none.
115710685Seric */
115810685Seric 
115910685Seric bool
116010685Seric bitzerop(map)
116110685Seric 	BITMAP map;
116210685Seric {
116310685Seric 	int i;
116410685Seric 
116510685Seric 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
116610685Seric 		if (map[i] != 0)
116710685Seric 			return (FALSE);
116810685Seric 	return (TRUE);
116910685Seric }
117058247Seric /*
117158318Seric **  STRCONTAINEDIN -- tell if one string is contained in another
117258318Seric **
117358318Seric **	Parameters:
117458318Seric **		a -- possible substring.
117558318Seric **		b -- possible superstring.
117658318Seric **
117758318Seric **	Returns:
117858318Seric **		TRUE if a is contained in b.
117958318Seric **		FALSE otherwise.
118058318Seric */
118158318Seric 
118258318Seric bool
118358318Seric strcontainedin(a, b)
118458318Seric 	register char *a;
118558318Seric 	register char *b;
118658318Seric {
118765012Seric 	int la;
118865012Seric 	int lb;
118965012Seric 	int c;
119058318Seric 
119165012Seric 	la = strlen(a);
119265012Seric 	lb = strlen(b);
119365012Seric 	c = *a;
119465012Seric 	if (isascii(c) && isupper(c))
119565012Seric 		c = tolower(c);
119665012Seric 	for (; lb-- >= la; b++)
119758318Seric 	{
119865012Seric 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
119965012Seric 			continue;
120065012Seric 		if (strncasecmp(a, b, la) == 0)
120158318Seric 			return TRUE;
120258318Seric 	}
120365012Seric 	return FALSE;
120458318Seric }
120563753Seric /*
120663753Seric **  CHECKFD012 -- check low numbered file descriptors
120763753Seric **
120863753Seric **	File descriptors 0, 1, and 2 should be open at all times.
120963753Seric **	This routine verifies that, and fixes it if not true.
121063753Seric **
121163753Seric **	Parameters:
121263753Seric **		where -- a tag printed if the assertion failed
121363753Seric **
121463753Seric **	Returns:
121563753Seric **		none
121663753Seric */
121763753Seric 
121863753Seric checkfd012(where)
121963753Seric 	char *where;
122063753Seric {
122163753Seric #ifdef XDEBUG
122263753Seric 	register int i;
122363753Seric 	struct stat stbuf;
122463753Seric 
122563753Seric 	for (i = 0; i < 3; i++)
122663753Seric 	{
122764735Seric 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
122863753Seric 		{
122963753Seric 			/* oops.... */
123063753Seric 			int fd;
123163753Seric 
123263753Seric 			syserr("%s: fd %d not open", where, i);
123363753Seric 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
123463753Seric 			if (fd != i)
123563753Seric 			{
123663753Seric 				(void) dup2(fd, i);
123763753Seric 				(void) close(fd);
123863753Seric 			}
123963753Seric 		}
124063753Seric 	}
124163937Seric #endif /* XDEBUG */
124263753Seric }
124364725Seric /*
124464725Seric **  PRINTOPENFDS -- print the open file descriptors (for debugging)
124564725Seric **
124664725Seric **	Parameters:
124764725Seric **		logit -- if set, send output to syslog; otherwise
124864725Seric **			print for debugging.
124964725Seric **
125064725Seric **	Returns:
125164725Seric **		none.
125264725Seric */
125364725Seric 
125464725Seric #include <netdb.h>
125564725Seric #include <arpa/inet.h>
125664725Seric 
125764725Seric printopenfds(logit)
125864725Seric 	bool logit;
125964725Seric {
126064725Seric 	register int fd;
126164743Seric 	extern int DtableSize;
126264743Seric 
126364743Seric 	for (fd = 0; fd < DtableSize; fd++)
126464743Seric 		dumpfd(fd, FALSE, logit);
126564743Seric }
126664743Seric /*
126764743Seric **  DUMPFD -- dump a file descriptor
126864743Seric **
126964743Seric **	Parameters:
127064743Seric **		fd -- the file descriptor to dump.
127164743Seric **		printclosed -- if set, print a notification even if
127264743Seric **			it is closed; otherwise print nothing.
127364743Seric **		logit -- if set, send output to syslog instead of stdout.
127464743Seric */
127564743Seric 
127664743Seric dumpfd(fd, printclosed, logit)
127764743Seric 	int fd;
127864743Seric 	bool printclosed;
127964743Seric 	bool logit;
128064743Seric {
128164725Seric 	register struct hostent *hp;
128264725Seric 	register char *p;
128366747Seric 	char *fmtstr;
128464743Seric 	struct sockaddr_in sin;
128564743Seric 	auto int slen;
128664743Seric 	struct stat st;
128764725Seric 	char buf[200];
128864725Seric 
128964743Seric 	p = buf;
129064743Seric 	sprintf(p, "%3d: ", fd);
129164743Seric 	p += strlen(p);
129264743Seric 
129364743Seric 	if (fstat(fd, &st) < 0)
129464725Seric 	{
129564743Seric 		if (printclosed || errno != EBADF)
129664743Seric 		{
129764743Seric 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
129864743Seric 			goto printit;
129964743Seric 		}
130064743Seric 		return;
130164743Seric 	}
130264725Seric 
130364743Seric 	slen = fcntl(fd, F_GETFL, NULL);
130464743Seric 	if (slen != -1)
130564743Seric 	{
130664743Seric 		sprintf(p, "fl=0x%x, ", slen);
130764743Seric 		p += strlen(p);
130864743Seric 	}
130964725Seric 
131064743Seric 	sprintf(p, "mode=%o: ", st.st_mode);
131164743Seric 	p += strlen(p);
131264743Seric 	switch (st.st_mode & S_IFMT)
131364743Seric 	{
131464807Seric #ifdef S_IFSOCK
131564743Seric 	  case S_IFSOCK:
131664743Seric 		sprintf(p, "SOCK ");
131764725Seric 		p += strlen(p);
131864743Seric 		slen = sizeof sin;
131964743Seric 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
132064743Seric 			sprintf(p, "(badsock)");
132164743Seric 		else
132264725Seric 		{
1323*68478Seric 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
132464743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
132564743Seric 						   : hp->h_name, ntohs(sin.sin_port));
132664743Seric 		}
132764743Seric 		p += strlen(p);
132864743Seric 		sprintf(p, "->");
132964743Seric 		p += strlen(p);
133064743Seric 		slen = sizeof sin;
133164743Seric 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
133264743Seric 			sprintf(p, "(badsock)");
133364743Seric 		else
133464743Seric 		{
1335*68478Seric 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
133664743Seric 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
133764743Seric 						   : hp->h_name, ntohs(sin.sin_port));
133864743Seric 		}
133964743Seric 		break;
134064807Seric #endif
134164725Seric 
134264743Seric 	  case S_IFCHR:
134364743Seric 		sprintf(p, "CHR: ");
134464743Seric 		p += strlen(p);
134564743Seric 		goto defprint;
134664725Seric 
134764743Seric 	  case S_IFBLK:
134864743Seric 		sprintf(p, "BLK: ");
134964743Seric 		p += strlen(p);
135064743Seric 		goto defprint;
135164725Seric 
135266252Seric #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
135365378Seric 	  case S_IFIFO:
135465378Seric 		sprintf(p, "FIFO: ");
135565378Seric 		p += strlen(p);
135665378Seric 		goto defprint;
135765378Seric #endif
135865378Seric 
135965378Seric #ifdef S_IFDIR
136065378Seric 	  case S_IFDIR:
136165378Seric 		sprintf(p, "DIR: ");
136265378Seric 		p += strlen(p);
136365378Seric 		goto defprint;
136465378Seric #endif
136565378Seric 
136665378Seric #ifdef S_IFLNK
136765378Seric 	  case S_IFLNK:
136865378Seric 		sprintf(p, "LNK: ");
136965378Seric 		p += strlen(p);
137065378Seric 		goto defprint;
137165378Seric #endif
137265378Seric 
137364743Seric 	  default:
137464725Seric defprint:
137566785Seric 		if (sizeof st.st_size > sizeof (long))
137666751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
137766747Seric 		else
137866751Seric 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
137966747Seric 		sprintf(p, fmtstr,
138064743Seric 			major(st.st_dev), minor(st.st_dev), st.st_ino,
138164743Seric 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
138264743Seric 		break;
138364743Seric 	}
138464725Seric 
138564743Seric printit:
138666748Seric #ifdef LOG
138764743Seric 	if (logit)
138865006Seric 		syslog(LOG_DEBUG, "%s", buf);
138964743Seric 	else
139066748Seric #endif
139164743Seric 		printf("%s\n", buf);
139264725Seric }
139365015Seric /*
139465015Seric **  SHORTENSTRING -- return short version of a string
139565015Seric **
139665015Seric **	If the string is already short, just return it.  If it is too
139765015Seric **	long, return the head and tail of the string.
139865015Seric **
139965015Seric **	Parameters:
140065015Seric **		s -- the string to shorten.
140165015Seric **		m -- the max length of the string.
140265015Seric **
140365015Seric **	Returns:
140465015Seric **		Either s or a short version of s.
140565015Seric */
140665015Seric 
140765015Seric #ifndef MAXSHORTSTR
140865055Seric # define MAXSHORTSTR	203
140965015Seric #endif
141065015Seric 
141165015Seric char *
141265015Seric shortenstring(s, m)
141365015Seric 	register char *s;
141465015Seric 	int m;
141565015Seric {
141665015Seric 	int l;
141765015Seric 	static char buf[MAXSHORTSTR + 1];
141865015Seric 
141965015Seric 	l = strlen(s);
142065015Seric 	if (l < m)
142165015Seric 		return s;
142265015Seric 	if (m > MAXSHORTSTR)
142365015Seric 		m = MAXSHORTSTR;
142465015Seric 	else if (m < 10)
142565015Seric 	{
142665015Seric 		if (m < 5)
142765015Seric 		{
142865015Seric 			strncpy(buf, s, m);
142965015Seric 			buf[m] = '\0';
143065015Seric 			return buf;
143165015Seric 		}
143265015Seric 		strncpy(buf, s, m - 3);
143365015Seric 		strcpy(buf + m - 3, "...");
143465015Seric 		return buf;
143565015Seric 	}
143665015Seric 	m = (m - 3) / 2;
143765015Seric 	strncpy(buf, s, m);
143865015Seric 	strcpy(buf + m, "...");
143965015Seric 	strcpy(buf + m + 3, s + l - m);
144065015Seric 	return buf;
144165015Seric }
144267848Seric /*
144368267Seric **  CLEANSTRCPY -- copy string keeping out bogus characters
144468267Seric **
144568267Seric **	Parameters:
144668267Seric **		t -- "to" string.
144768267Seric **		f -- "from" string.
144868267Seric **		l -- length of space available in "to" string.
144968267Seric **
145068267Seric **	Returns:
145168267Seric **		none.
145268267Seric */
145368267Seric 
145468267Seric void
145568267Seric cleanstrcpy(t, f, l)
145668267Seric 	register char *t;
145768267Seric 	register char *f;
145868267Seric 	int l;
145968267Seric {
146068281Seric #ifdef LOG
146168281Seric 	/* check for newlines and log if necessary */
1462*68478Seric 	(void) denlstring(f, TRUE, TRUE);
146368281Seric #endif
146468281Seric 
146568267Seric 	l--;
146668267Seric 	while (l > 0 && *f != '\0')
146768267Seric 	{
146868267Seric 		if (isascii(*f) &&
146968267Seric 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
147068267Seric 		{
147168267Seric 			l--;
147268267Seric 			*t++ = *f;
147368267Seric 		}
147468267Seric 		f++;
147568267Seric 	}
147668267Seric 	*t = '\0';
147768267Seric }
147868267Seric /*
147968267Seric **  DENLSTRING -- convert newlines in a string to spaces
148068267Seric **
148168267Seric **	Parameters:
148268267Seric **		s -- the input string
1483*68478Seric **		strict -- if set, don't permit continuation lines.
148468457Seric **		logattacks -- if set, log attempted attacks.
148568267Seric **
148668267Seric **	Returns:
148768267Seric **		A pointer to a version of the string with newlines
148868267Seric **		mapped to spaces.  This should be copied.
148968267Seric */
149068267Seric 
149168267Seric char *
1492*68478Seric denlstring(s, strict, logattacks)
149368267Seric 	char *s;
1494*68478Seric 	int strict;
149568465Seric 	int logattacks;
149668267Seric {
149768267Seric 	register char *p;
149868267Seric 	int l;
149968267Seric 	static char *bp = NULL;
150068267Seric 	static int bl = 0;
150168267Seric 
1502*68478Seric 	p = s;
1503*68478Seric 	while ((p = strchr(p, '\n')) != NULL)
1504*68478Seric 		if (strict || (*++p != ' ' && *p != '\t'))
1505*68478Seric 			break;
1506*68478Seric 	if (p == NULL)
150768267Seric 		return s;
150868267Seric 
150968267Seric 	l = strlen(s) + 1;
151068267Seric 	if (bl < l)
151168267Seric 	{
151268267Seric 		/* allocate more space */
151368267Seric 		if (bp != NULL)
151468267Seric 			free(bp);
151568267Seric 		bp = xalloc(l);
151668267Seric 		bl = l;
151768267Seric 	}
151868267Seric 	strcpy(bp, s);
151968267Seric 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
152068267Seric 		*p++ = ' ';
152168281Seric 
152268281Seric #ifdef LOG
152368457Seric 	if (logattacks)
152468457Seric 	{
152568457Seric 		syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
152668457Seric 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
152768457Seric 			shortenstring(bp, 80));
152868457Seric 	}
152968281Seric #endif
153068281Seric 
153168267Seric 	return bp;
153268267Seric }
1533