xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision 4c0050d5)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	8.49 (Berkeley) 02/11/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sysexits.h>
15 /*
16 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17 **
18 **	Runs through a string and strips off unquoted quote
19 **	characters and quote bits.  This is done in place.
20 **
21 **	Parameters:
22 **		s -- the string to strip.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		none.
29 **
30 **	Called By:
31 **		deliver
32 */
33 
34 stripquotes(s)
35 	char *s;
36 {
37 	register char *p;
38 	register char *q;
39 	register char c;
40 
41 	if (s == NULL)
42 		return;
43 
44 	p = q = s;
45 	do
46 	{
47 		c = *p++;
48 		if (c == '\\')
49 			c = *p++;
50 		else if (c == '"')
51 			continue;
52 		*q++ = c;
53 	} while (c != '\0');
54 }
55 /*
56 **  XALLOC -- Allocate memory and bitch wildly on failure.
57 **
58 **	THIS IS A CLUDGE.  This should be made to give a proper
59 **	error -- but after all, what can we do?
60 **
61 **	Parameters:
62 **		sz -- size of area to allocate.
63 **
64 **	Returns:
65 **		pointer to data region.
66 **
67 **	Side Effects:
68 **		Memory is allocated.
69 */
70 
71 char *
72 xalloc(sz)
73 	register int sz;
74 {
75 	register char *p;
76 
77 	/* some systems can't handle size zero mallocs */
78 	if (sz <= 0)
79 		sz = 1;
80 
81 	p = malloc((unsigned) sz);
82 	if (p == NULL)
83 	{
84 		syserr("Out of memory!!");
85 		abort();
86 		/* exit(EX_UNAVAILABLE); */
87 	}
88 	return (p);
89 }
90 /*
91 **  COPYPLIST -- copy list of pointers.
92 **
93 **	This routine is the equivalent of newstr for lists of
94 **	pointers.
95 **
96 **	Parameters:
97 **		list -- list of pointers to copy.
98 **			Must be NULL terminated.
99 **		copycont -- if TRUE, copy the contents of the vector
100 **			(which must be a string) also.
101 **
102 **	Returns:
103 **		a copy of 'list'.
104 **
105 **	Side Effects:
106 **		none.
107 */
108 
109 char **
110 copyplist(list, copycont)
111 	char **list;
112 	bool copycont;
113 {
114 	register char **vp;
115 	register char **newvp;
116 
117 	for (vp = list; *vp != NULL; vp++)
118 		continue;
119 
120 	vp++;
121 
122 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
123 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
124 
125 	if (copycont)
126 	{
127 		for (vp = newvp; *vp != NULL; vp++)
128 			*vp = newstr(*vp);
129 	}
130 
131 	return (newvp);
132 }
133 /*
134 **  COPYQUEUE -- copy address queue.
135 **
136 **	This routine is the equivalent of newstr for address queues
137 **	addresses marked with QDONTSEND aren't copied
138 **
139 **	Parameters:
140 **		addr -- list of address structures to copy.
141 **
142 **	Returns:
143 **		a copy of 'addr'.
144 **
145 **	Side Effects:
146 **		none.
147 */
148 
149 ADDRESS *
150 copyqueue(addr)
151 	ADDRESS *addr;
152 {
153 	register ADDRESS *newaddr;
154 	ADDRESS *ret;
155 	register ADDRESS **tail = &ret;
156 
157 	while (addr != NULL)
158 	{
159 		if (!bitset(QDONTSEND, addr->q_flags))
160 		{
161 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
162 			STRUCTCOPY(*addr, *newaddr);
163 			*tail = newaddr;
164 			tail = &newaddr->q_next;
165 		}
166 		addr = addr->q_next;
167 	}
168 	*tail = NULL;
169 
170 	return ret;
171 }
172 /*
173 **  PRINTAV -- print argument vector.
174 **
175 **	Parameters:
176 **		av -- argument vector.
177 **
178 **	Returns:
179 **		none.
180 **
181 **	Side Effects:
182 **		prints av.
183 */
184 
185 printav(av)
186 	register char **av;
187 {
188 	while (*av != NULL)
189 	{
190 		if (tTd(0, 44))
191 			printf("\n\t%08x=", *av);
192 		else
193 			(void) putchar(' ');
194 		xputs(*av++);
195 	}
196 	(void) putchar('\n');
197 }
198 /*
199 **  LOWER -- turn letter into lower case.
200 **
201 **	Parameters:
202 **		c -- character to turn into lower case.
203 **
204 **	Returns:
205 **		c, in lower case.
206 **
207 **	Side Effects:
208 **		none.
209 */
210 
211 char
212 lower(c)
213 	register char c;
214 {
215 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
216 }
217 /*
218 **  XPUTS -- put string doing control escapes.
219 **
220 **	Parameters:
221 **		s -- string to put.
222 **
223 **	Returns:
224 **		none.
225 **
226 **	Side Effects:
227 **		output to stdout
228 */
229 
230 xputs(s)
231 	register char *s;
232 {
233 	register int c;
234 	register struct metamac *mp;
235 	extern struct metamac MetaMacros[];
236 
237 	if (s == NULL)
238 	{
239 		printf("<null>");
240 		return;
241 	}
242 	while ((c = (*s++ & 0377)) != '\0')
243 	{
244 		if (!isascii(c))
245 		{
246 			if (c == MATCHREPL)
247 			{
248 				putchar('$');
249 				continue;
250 			}
251 			if (c == MACROEXPAND)
252 			{
253 				putchar('$');
254 				if (bitset(0200, *s))
255 					printf("{%s}", macname(*s++ & 0377));
256 				continue;
257 			}
258 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
259 			{
260 				if ((mp->metaval & 0377) == c)
261 				{
262 					printf("$%c", mp->metaname);
263 					break;
264 				}
265 			}
266 			if (mp->metaname != '\0')
267 				continue;
268 			(void) putchar('\\');
269 			c &= 0177;
270 		}
271 		if (isprint(c))
272 		{
273 			putchar(c);
274 			continue;
275 		}
276 
277 		/* wasn't a meta-macro -- find another way to print it */
278 		switch (c)
279 		{
280 		  case '\0':
281 			continue;
282 
283 		  case '\n':
284 			c = 'n';
285 			break;
286 
287 		  case '\r':
288 			c = 'r';
289 			break;
290 
291 		  case '\t':
292 			c = 't';
293 			break;
294 
295 		  default:
296 			(void) putchar('^');
297 			(void) putchar(c ^ 0100);
298 			continue;
299 		}
300 	}
301 	(void) fflush(stdout);
302 }
303 /*
304 **  MAKELOWER -- Translate a line into lower case
305 **
306 **	Parameters:
307 **		p -- the string to translate.  If NULL, return is
308 **			immediate.
309 **
310 **	Returns:
311 **		none.
312 **
313 **	Side Effects:
314 **		String pointed to by p is translated to lower case.
315 **
316 **	Called By:
317 **		parse
318 */
319 
320 makelower(p)
321 	register char *p;
322 {
323 	register char c;
324 
325 	if (p == NULL)
326 		return;
327 	for (; (c = *p) != '\0'; p++)
328 		if (isascii(c) && isupper(c))
329 			*p = tolower(c);
330 }
331 /*
332 **  BUILDFNAME -- build full name from gecos style entry.
333 **
334 **	This routine interprets the strange entry that would appear
335 **	in the GECOS field of the password file.
336 **
337 **	Parameters:
338 **		p -- name to build.
339 **		login -- the login name of this user (for &).
340 **		buf -- place to put the result.
341 **
342 **	Returns:
343 **		none.
344 **
345 **	Side Effects:
346 **		none.
347 */
348 
349 buildfname(gecos, login, buf)
350 	register char *gecos;
351 	char *login;
352 	char *buf;
353 {
354 	register char *p;
355 	register char *bp = buf;
356 	int l;
357 
358 	if (*gecos == '*')
359 		gecos++;
360 
361 	/* find length of final string */
362 	l = 0;
363 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
364 	{
365 		if (*p == '&')
366 			l += strlen(login);
367 		else
368 			l++;
369 	}
370 
371 	/* now fill in buf */
372 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
373 	{
374 		if (*p == '&')
375 		{
376 			(void) strcpy(bp, login);
377 			*bp = toupper(*bp);
378 			while (*bp != '\0')
379 				bp++;
380 		}
381 		else
382 			*bp++ = *p;
383 	}
384 	*bp = '\0';
385 }
386 /*
387 **  SAFEFILE -- return true if a file exists and is safe for a user.
388 **
389 **	Parameters:
390 **		fn -- filename to check.
391 **		uid -- user id to compare against.
392 **		gid -- group id to compare against.
393 **		uname -- user name to compare against (used for group
394 **			sets).
395 **		flags -- modifiers:
396 **			SFF_MUSTOWN -- "uid" must own this file.
397 **			SFF_NOSLINK -- file cannot be a symbolic link.
398 **		mode -- mode bits that must match.
399 **
400 **	Returns:
401 **		0 if fn exists, is owned by uid, and matches mode.
402 **		An errno otherwise.  The actual errno is cleared.
403 **
404 **	Side Effects:
405 **		none.
406 */
407 
408 #include <grp.h>
409 
410 #ifndef S_IXOTH
411 # define S_IXOTH	(S_IEXEC >> 6)
412 #endif
413 
414 #ifndef S_IXGRP
415 # define S_IXGRP	(S_IEXEC >> 3)
416 #endif
417 
418 #ifndef S_IXUSR
419 # define S_IXUSR	(S_IEXEC)
420 #endif
421 
422 int
423 safefile(fn, uid, gid, uname, flags, mode)
424 	char *fn;
425 	uid_t uid;
426 	gid_t gid;
427 	char *uname;
428 	int flags;
429 	int mode;
430 {
431 	register char *p;
432 	register struct group *gr = NULL;
433 	struct stat stbuf;
434 
435 	if (tTd(54, 4))
436 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
437 			fn, uid, gid, flags, mode);
438 	errno = 0;
439 
440 	if (!bitset(SFF_NOPATHCHECK, flags) ||
441 	    (uid == 0 && !bitset(SFF_ROOTOK, flags)))
442 	{
443 		/* check the path to the file for acceptability */
444 		for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
445 		{
446 			*p = '\0';
447 			if (stat(fn, &stbuf) < 0)
448 				break;
449 			if (uid == 0 && !bitset(SFF_ROOTOK, flags))
450 			{
451 				if (bitset(S_IXOTH, stbuf.st_mode))
452 					continue;
453 				break;
454 			}
455 			if (stbuf.st_uid == uid &&
456 			    bitset(S_IXUSR, stbuf.st_mode))
457 				continue;
458 			if (stbuf.st_gid == gid &&
459 			    bitset(S_IXGRP, stbuf.st_mode))
460 				continue;
461 #ifndef NO_GROUP_SET
462 			if (uname != NULL &&
463 			    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
464 			     (gr = getgrgid(stbuf.st_gid)) != NULL))
465 			{
466 				register char **gp;
467 
468 				for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
469 					if (strcmp(*gp, uname) == 0)
470 						break;
471 				if (gp != NULL && *gp != NULL &&
472 				    bitset(S_IXGRP, stbuf.st_mode))
473 					continue;
474 			}
475 #endif
476 			if (!bitset(S_IXOTH, stbuf.st_mode))
477 				break;
478 		}
479 		if (p != NULL)
480 		{
481 			int ret = errno;
482 
483 			if (ret == 0)
484 				ret = EACCES;
485 			if (tTd(54, 4))
486 				printf("\t[dir %s] %s\n", fn, errstring(ret));
487 			*p = '/';
488 			return ret;
489 		}
490 	}
491 
492 #ifdef HASLSTAT
493 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
494 					: stat(fn, &stbuf)) < 0)
495 #else
496 	if (stat(fn, &stbuf) < 0)
497 #endif
498 	{
499 		int ret = errno;
500 
501 		if (tTd(54, 4))
502 			printf("\t%s\n", errstring(ret));
503 
504 		errno = 0;
505 		return ret;
506 	}
507 
508 #ifdef S_ISLNK
509 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
510 	{
511 		if (tTd(54, 4))
512 			printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
513 		return EPERM;
514 	}
515 #endif
516 
517 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
518 		mode >>= 6;
519 	else if (stbuf.st_uid != uid)
520 	{
521 		mode >>= 3;
522 		if (stbuf.st_gid == gid)
523 			;
524 #ifndef NO_GROUP_SET
525 		else if (uname != NULL &&
526 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
527 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
528 		{
529 			register char **gp;
530 
531 			for (gp = gr->gr_mem; *gp != NULL; gp++)
532 				if (strcmp(*gp, uname) == 0)
533 					break;
534 			if (*gp == NULL)
535 				mode >>= 3;
536 		}
537 #endif
538 		else
539 			mode >>= 3;
540 	}
541 	if (tTd(54, 4))
542 		printf("\t[uid %d, stat %o, mode %o] ",
543 			stbuf.st_uid, stbuf.st_mode, mode);
544 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
545 	     !bitset(SFF_MUSTOWN, flags)) &&
546 	    (stbuf.st_mode & mode) == mode)
547 	{
548 		if (tTd(54, 4))
549 			printf("\tOK\n");
550 		return 0;
551 	}
552 	if (tTd(54, 4))
553 		printf("\tEACCES\n");
554 	return EACCES;
555 }
556 /*
557 **  FIXCRLF -- fix <CR><LF> in line.
558 **
559 **	Looks for the <CR><LF> combination and turns it into the
560 **	UNIX canonical <NL> character.  It only takes one line,
561 **	i.e., it is assumed that the first <NL> found is the end
562 **	of the line.
563 **
564 **	Parameters:
565 **		line -- the line to fix.
566 **		stripnl -- if true, strip the newline also.
567 **
568 **	Returns:
569 **		none.
570 **
571 **	Side Effects:
572 **		line is changed in place.
573 */
574 
575 fixcrlf(line, stripnl)
576 	char *line;
577 	bool stripnl;
578 {
579 	register char *p;
580 
581 	p = strchr(line, '\n');
582 	if (p == NULL)
583 		return;
584 	if (p > line && p[-1] == '\r')
585 		p--;
586 	if (!stripnl)
587 		*p++ = '\n';
588 	*p = '\0';
589 }
590 /*
591 **  DFOPEN -- determined file open
592 **
593 **	This routine has the semantics of fopen, except that it will
594 **	keep trying a few times to make this happen.  The idea is that
595 **	on very loaded systems, we may run out of resources (inodes,
596 **	whatever), so this tries to get around it.
597 */
598 
599 #ifndef O_ACCMODE
600 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
601 #endif
602 
603 struct omodes
604 {
605 	int	mask;
606 	int	mode;
607 	char	*farg;
608 } OpenModes[] =
609 {
610 	O_ACCMODE,		O_RDONLY,		"r",
611 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
612 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
613 	O_TRUNC,		0,			"w+",
614 	O_APPEND,		O_APPEND,		"a+",
615 	0,			0,			"r+",
616 };
617 
618 FILE *
619 dfopen(filename, omode, cmode)
620 	char *filename;
621 	int omode;
622 	int cmode;
623 {
624 	register int tries;
625 	int fd;
626 	register struct omodes *om;
627 	struct stat st;
628 
629 	for (om = OpenModes; om->mask != 0; om++)
630 		if ((omode & om->mask) == om->mode)
631 			break;
632 
633 	for (tries = 0; tries < 10; tries++)
634 	{
635 		sleep((unsigned) (10 * tries));
636 		errno = 0;
637 		fd = open(filename, omode, cmode);
638 		if (fd >= 0)
639 			break;
640 		switch (errno)
641 		{
642 		  case ENFILE:		/* system file table full */
643 		  case EINTR:		/* interrupted syscall */
644 #ifdef ETXTBSY
645 		  case ETXTBSY:		/* Apollo: net file locked */
646 #endif
647 			continue;
648 		}
649 		break;
650 	}
651 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
652 	{
653 		int locktype;
654 
655 		/* lock the file to avoid accidental conflicts */
656 		if ((omode & O_ACCMODE) != O_RDONLY)
657 			locktype = LOCK_EX;
658 		else
659 			locktype = LOCK_SH;
660 		(void) lockfile(fd, filename, NULL, locktype);
661 		errno = 0;
662 	}
663 	if (fd < 0)
664 		return NULL;
665 	else
666 		return fdopen(fd, om->farg);
667 }
668 /*
669 **  PUTLINE -- put a line like fputs obeying SMTP conventions
670 **
671 **	This routine always guarantees outputing a newline (or CRLF,
672 **	as appropriate) at the end of the string.
673 **
674 **	Parameters:
675 **		l -- line to put.
676 **		mci -- the mailer connection information.
677 **
678 **	Returns:
679 **		none
680 **
681 **	Side Effects:
682 **		output of l to fp.
683 */
684 
685 putline(l, mci)
686 	register char *l;
687 	register MCI *mci;
688 {
689 	register char *p;
690 	register char svchar;
691 	int slop = 0;
692 
693 	/* strip out 0200 bits -- these can look like TELNET protocol */
694 	if (bitset(MCIF_7BIT, mci->mci_flags))
695 	{
696 		for (p = l; (svchar = *p) != '\0'; ++p)
697 			if (bitset(0200, svchar))
698 				*p = svchar &~ 0200;
699 	}
700 
701 	do
702 	{
703 		/* find the end of the line */
704 		p = strchr(l, '\n');
705 		if (p == NULL)
706 			p = &l[strlen(l)];
707 
708 		if (TrafficLogFile != NULL)
709 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
710 
711 		/* check for line overflow */
712 		while (mci->mci_mailer->m_linelimit > 0 &&
713 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
714 		{
715 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
716 
717 			svchar = *q;
718 			*q = '\0';
719 			if (l[0] == '.' && slop == 0 &&
720 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
721 			{
722 				(void) putc('.', mci->mci_out);
723 				if (TrafficLogFile != NULL)
724 					(void) putc('.', TrafficLogFile);
725 			}
726 			fputs(l, mci->mci_out);
727 			(void) putc('!', mci->mci_out);
728 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
729 			(void) putc(' ', mci->mci_out);
730 			if (TrafficLogFile != NULL)
731 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
732 					l, getpid());
733 			*q = svchar;
734 			l = q;
735 			slop = 1;
736 		}
737 
738 		/* output last part */
739 		if (l[0] == '.' && slop == 0 &&
740 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
741 		{
742 			(void) putc('.', mci->mci_out);
743 			if (TrafficLogFile != NULL)
744 				(void) putc('.', TrafficLogFile);
745 		}
746 		if (TrafficLogFile != NULL)
747 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
748 		for ( ; l < p; ++l)
749 			(void) putc(*l, mci->mci_out);
750 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
751 		if (*l == '\n')
752 			++l;
753 	} while (l[0] != '\0');
754 }
755 /*
756 **  XUNLINK -- unlink a file, doing logging as appropriate.
757 **
758 **	Parameters:
759 **		f -- name of file to unlink.
760 **
761 **	Returns:
762 **		none.
763 **
764 **	Side Effects:
765 **		f is unlinked.
766 */
767 
768 xunlink(f)
769 	char *f;
770 {
771 	register int i;
772 
773 # ifdef LOG
774 	if (LogLevel > 98)
775 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
776 # endif /* LOG */
777 
778 	i = unlink(f);
779 # ifdef LOG
780 	if (i < 0 && LogLevel > 97)
781 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
782 # endif /* LOG */
783 }
784 /*
785 **  XFCLOSE -- close a file, doing logging as appropriate.
786 **
787 **	Parameters:
788 **		fp -- file pointer for the file to close
789 **		a, b -- miscellaneous crud to print for debugging
790 **
791 **	Returns:
792 **		none.
793 **
794 **	Side Effects:
795 **		fp is closed.
796 */
797 
798 xfclose(fp, a, b)
799 	FILE *fp;
800 	char *a, *b;
801 {
802 	if (tTd(53, 99))
803 		printf("xfclose(%x) %s %s\n", fp, a, b);
804 #ifdef XDEBUG
805 	if (fileno(fp) == 1)
806 		syserr("xfclose(%s %s): fd = 1", a, b);
807 #endif
808 	if (fclose(fp) < 0 && tTd(53, 99))
809 		printf("xfclose FAILURE: %s\n", errstring(errno));
810 }
811 /*
812 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
813 **
814 **	Parameters:
815 **		buf -- place to put the input line.
816 **		siz -- size of buf.
817 **		fp -- file to read from.
818 **		timeout -- the timeout before error occurs.
819 **		during -- what we are trying to read (for error messages).
820 **
821 **	Returns:
822 **		NULL on error (including timeout).  This will also leave
823 **			buf containing a null string.
824 **		buf otherwise.
825 **
826 **	Side Effects:
827 **		none.
828 */
829 
830 static jmp_buf	CtxReadTimeout;
831 static int	readtimeout();
832 
833 char *
834 sfgets(buf, siz, fp, timeout, during)
835 	char *buf;
836 	int siz;
837 	FILE *fp;
838 	time_t timeout;
839 	char *during;
840 {
841 	register EVENT *ev = NULL;
842 	register char *p;
843 
844 	if (fp == NULL)
845 	{
846 		buf[0] = '\0';
847 		return NULL;
848 	}
849 
850 	/* set the timeout */
851 	if (timeout != 0)
852 	{
853 		if (setjmp(CtxReadTimeout) != 0)
854 		{
855 # ifdef LOG
856 			syslog(LOG_NOTICE,
857 			    "timeout waiting for input from %s during %s\n",
858 			    CurHostName? CurHostName: "local", during);
859 # endif
860 			errno = 0;
861 			usrerr("451 timeout waiting for input during %s",
862 				during);
863 			buf[0] = '\0';
864 #ifdef XDEBUG
865 			checkfd012(during);
866 #endif
867 			return (NULL);
868 		}
869 		ev = setevent(timeout, readtimeout, 0);
870 	}
871 
872 	/* try to read */
873 	p = NULL;
874 	while (!feof(fp) && !ferror(fp))
875 	{
876 		errno = 0;
877 		p = fgets(buf, siz, fp);
878 		if (p != NULL || errno != EINTR)
879 			break;
880 		clearerr(fp);
881 	}
882 
883 	/* clear the event if it has not sprung */
884 	clrevent(ev);
885 
886 	/* clean up the books and exit */
887 	LineNumber++;
888 	if (p == NULL)
889 	{
890 		buf[0] = '\0';
891 		if (TrafficLogFile != NULL)
892 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
893 		return (NULL);
894 	}
895 	if (TrafficLogFile != NULL)
896 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
897 	if (SevenBitInput)
898 	{
899 		for (p = buf; *p != '\0'; p++)
900 			*p &= ~0200;
901 	}
902 	else if (!HasEightBits)
903 	{
904 		for (p = buf; *p != '\0'; p++)
905 		{
906 			if (bitset(0200, *p))
907 			{
908 				HasEightBits = TRUE;
909 				break;
910 			}
911 		}
912 	}
913 	return (buf);
914 }
915 
916 static
917 readtimeout(timeout)
918 	time_t timeout;
919 {
920 	longjmp(CtxReadTimeout, 1);
921 }
922 /*
923 **  FGETFOLDED -- like fgets, but know about folded lines.
924 **
925 **	Parameters:
926 **		buf -- place to put result.
927 **		n -- bytes available.
928 **		f -- file to read from.
929 **
930 **	Returns:
931 **		input line(s) on success, NULL on error or EOF.
932 **		This will normally be buf -- unless the line is too
933 **			long, when it will be xalloc()ed.
934 **
935 **	Side Effects:
936 **		buf gets lines from f, with continuation lines (lines
937 **		with leading white space) appended.  CRLF's are mapped
938 **		into single newlines.  Any trailing NL is stripped.
939 */
940 
941 char *
942 fgetfolded(buf, n, f)
943 	char *buf;
944 	register int n;
945 	FILE *f;
946 {
947 	register char *p = buf;
948 	char *bp = buf;
949 	register int i;
950 
951 	n--;
952 	while ((i = getc(f)) != EOF)
953 	{
954 		if (i == '\r')
955 		{
956 			i = getc(f);
957 			if (i != '\n')
958 			{
959 				if (i != EOF)
960 					(void) ungetc(i, f);
961 				i = '\r';
962 			}
963 		}
964 		if (--n <= 0)
965 		{
966 			/* allocate new space */
967 			char *nbp;
968 			int nn;
969 
970 			nn = (p - bp);
971 			if (nn < MEMCHUNKSIZE)
972 				nn *= 2;
973 			else
974 				nn += MEMCHUNKSIZE;
975 			nbp = xalloc(nn);
976 			bcopy(bp, nbp, p - bp);
977 			p = &nbp[p - bp];
978 			if (bp != buf)
979 				free(bp);
980 			bp = nbp;
981 			n = nn - (p - bp);
982 		}
983 		*p++ = i;
984 		if (i == '\n')
985 		{
986 			LineNumber++;
987 			i = getc(f);
988 			if (i != EOF)
989 				(void) ungetc(i, f);
990 			if (i != ' ' && i != '\t')
991 				break;
992 		}
993 	}
994 	if (p == bp)
995 		return (NULL);
996 	*--p = '\0';
997 	return (bp);
998 }
999 /*
1000 **  CURTIME -- return current time.
1001 **
1002 **	Parameters:
1003 **		none.
1004 **
1005 **	Returns:
1006 **		the current time.
1007 **
1008 **	Side Effects:
1009 **		none.
1010 */
1011 
1012 time_t
1013 curtime()
1014 {
1015 	auto time_t t;
1016 
1017 	(void) time(&t);
1018 	return (t);
1019 }
1020 /*
1021 **  ATOBOOL -- convert a string representation to boolean.
1022 **
1023 **	Defaults to "TRUE"
1024 **
1025 **	Parameters:
1026 **		s -- string to convert.  Takes "tTyY" as true,
1027 **			others as false.
1028 **
1029 **	Returns:
1030 **		A boolean representation of the string.
1031 **
1032 **	Side Effects:
1033 **		none.
1034 */
1035 
1036 bool
1037 atobool(s)
1038 	register char *s;
1039 {
1040 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1041 		return (TRUE);
1042 	return (FALSE);
1043 }
1044 /*
1045 **  ATOOCT -- convert a string representation to octal.
1046 **
1047 **	Parameters:
1048 **		s -- string to convert.
1049 **
1050 **	Returns:
1051 **		An integer representing the string interpreted as an
1052 **		octal number.
1053 **
1054 **	Side Effects:
1055 **		none.
1056 */
1057 
1058 atooct(s)
1059 	register char *s;
1060 {
1061 	register int i = 0;
1062 
1063 	while (*s >= '0' && *s <= '7')
1064 		i = (i << 3) | (*s++ - '0');
1065 	return (i);
1066 }
1067 /*
1068 **  WAITFOR -- wait for a particular process id.
1069 **
1070 **	Parameters:
1071 **		pid -- process id to wait for.
1072 **
1073 **	Returns:
1074 **		status of pid.
1075 **		-1 if pid never shows up.
1076 **
1077 **	Side Effects:
1078 **		none.
1079 */
1080 
1081 int
1082 waitfor(pid)
1083 	int pid;
1084 {
1085 #ifdef WAITUNION
1086 	union wait st;
1087 #else
1088 	auto int st;
1089 #endif
1090 	int i;
1091 
1092 	do
1093 	{
1094 		errno = 0;
1095 		i = wait(&st);
1096 	} while ((i >= 0 || errno == EINTR) && i != pid);
1097 	if (i < 0)
1098 		return -1;
1099 #ifdef WAITUNION
1100 	return st.w_status;
1101 #else
1102 	return st;
1103 #endif
1104 }
1105 /*
1106 **  BITINTERSECT -- tell if two bitmaps intersect
1107 **
1108 **	Parameters:
1109 **		a, b -- the bitmaps in question
1110 **
1111 **	Returns:
1112 **		TRUE if they have a non-null intersection
1113 **		FALSE otherwise
1114 **
1115 **	Side Effects:
1116 **		none.
1117 */
1118 
1119 bool
1120 bitintersect(a, b)
1121 	BITMAP a;
1122 	BITMAP b;
1123 {
1124 	int i;
1125 
1126 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1127 		if ((a[i] & b[i]) != 0)
1128 			return (TRUE);
1129 	return (FALSE);
1130 }
1131 /*
1132 **  BITZEROP -- tell if a bitmap is all zero
1133 **
1134 **	Parameters:
1135 **		map -- the bit map to check
1136 **
1137 **	Returns:
1138 **		TRUE if map is all zero.
1139 **		FALSE if there are any bits set in map.
1140 **
1141 **	Side Effects:
1142 **		none.
1143 */
1144 
1145 bool
1146 bitzerop(map)
1147 	BITMAP map;
1148 {
1149 	int i;
1150 
1151 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1152 		if (map[i] != 0)
1153 			return (FALSE);
1154 	return (TRUE);
1155 }
1156 /*
1157 **  STRCONTAINEDIN -- tell if one string is contained in another
1158 **
1159 **	Parameters:
1160 **		a -- possible substring.
1161 **		b -- possible superstring.
1162 **
1163 **	Returns:
1164 **		TRUE if a is contained in b.
1165 **		FALSE otherwise.
1166 */
1167 
1168 bool
1169 strcontainedin(a, b)
1170 	register char *a;
1171 	register char *b;
1172 {
1173 	int la;
1174 	int lb;
1175 	int c;
1176 
1177 	la = strlen(a);
1178 	lb = strlen(b);
1179 	c = *a;
1180 	if (isascii(c) && isupper(c))
1181 		c = tolower(c);
1182 	for (; lb-- >= la; b++)
1183 	{
1184 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1185 			continue;
1186 		if (strncasecmp(a, b, la) == 0)
1187 			return TRUE;
1188 	}
1189 	return FALSE;
1190 }
1191 /*
1192 **  CHECKFD012 -- check low numbered file descriptors
1193 **
1194 **	File descriptors 0, 1, and 2 should be open at all times.
1195 **	This routine verifies that, and fixes it if not true.
1196 **
1197 **	Parameters:
1198 **		where -- a tag printed if the assertion failed
1199 **
1200 **	Returns:
1201 **		none
1202 */
1203 
1204 checkfd012(where)
1205 	char *where;
1206 {
1207 #ifdef XDEBUG
1208 	register int i;
1209 	struct stat stbuf;
1210 
1211 	for (i = 0; i < 3; i++)
1212 	{
1213 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1214 		{
1215 			/* oops.... */
1216 			int fd;
1217 
1218 			syserr("%s: fd %d not open", where, i);
1219 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1220 			if (fd != i)
1221 			{
1222 				(void) dup2(fd, i);
1223 				(void) close(fd);
1224 			}
1225 		}
1226 	}
1227 #endif /* XDEBUG */
1228 }
1229 /*
1230 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1231 **
1232 **	Parameters:
1233 **		logit -- if set, send output to syslog; otherwise
1234 **			print for debugging.
1235 **
1236 **	Returns:
1237 **		none.
1238 */
1239 
1240 #include <netdb.h>
1241 #include <arpa/inet.h>
1242 
1243 printopenfds(logit)
1244 	bool logit;
1245 {
1246 	register int fd;
1247 	extern int DtableSize;
1248 
1249 	for (fd = 0; fd < DtableSize; fd++)
1250 		dumpfd(fd, FALSE, logit);
1251 }
1252 /*
1253 **  DUMPFD -- dump a file descriptor
1254 **
1255 **	Parameters:
1256 **		fd -- the file descriptor to dump.
1257 **		printclosed -- if set, print a notification even if
1258 **			it is closed; otherwise print nothing.
1259 **		logit -- if set, send output to syslog instead of stdout.
1260 */
1261 
1262 dumpfd(fd, printclosed, logit)
1263 	int fd;
1264 	bool printclosed;
1265 	bool logit;
1266 {
1267 	register struct hostent *hp;
1268 	register char *p;
1269 	char *fmtstr;
1270 	struct sockaddr_in sin;
1271 	auto int slen;
1272 	struct stat st;
1273 	char buf[200];
1274 
1275 	p = buf;
1276 	sprintf(p, "%3d: ", fd);
1277 	p += strlen(p);
1278 
1279 	if (fstat(fd, &st) < 0)
1280 	{
1281 		if (printclosed || errno != EBADF)
1282 		{
1283 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1284 			goto printit;
1285 		}
1286 		return;
1287 	}
1288 
1289 	slen = fcntl(fd, F_GETFL, NULL);
1290 	if (slen != -1)
1291 	{
1292 		sprintf(p, "fl=0x%x, ", slen);
1293 		p += strlen(p);
1294 	}
1295 
1296 	sprintf(p, "mode=%o: ", st.st_mode);
1297 	p += strlen(p);
1298 	switch (st.st_mode & S_IFMT)
1299 	{
1300 #ifdef S_IFSOCK
1301 	  case S_IFSOCK:
1302 		sprintf(p, "SOCK ");
1303 		p += strlen(p);
1304 		slen = sizeof sin;
1305 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
1306 			sprintf(p, "(badsock)");
1307 		else
1308 		{
1309 			hp = gethostbyaddr((char *) &sin.sin_addr,
1310 					   INADDRSZ, AF_INET);
1311 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1312 						   : hp->h_name, ntohs(sin.sin_port));
1313 		}
1314 		p += strlen(p);
1315 		sprintf(p, "->");
1316 		p += strlen(p);
1317 		slen = sizeof sin;
1318 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
1319 			sprintf(p, "(badsock)");
1320 		else
1321 		{
1322 			hp = gethostbyaddr((char *) &sin.sin_addr,
1323 					   INADDRSZ, AF_INET);
1324 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1325 						   : hp->h_name, ntohs(sin.sin_port));
1326 		}
1327 		break;
1328 #endif
1329 
1330 	  case S_IFCHR:
1331 		sprintf(p, "CHR: ");
1332 		p += strlen(p);
1333 		goto defprint;
1334 
1335 	  case S_IFBLK:
1336 		sprintf(p, "BLK: ");
1337 		p += strlen(p);
1338 		goto defprint;
1339 
1340 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1341 	  case S_IFIFO:
1342 		sprintf(p, "FIFO: ");
1343 		p += strlen(p);
1344 		goto defprint;
1345 #endif
1346 
1347 #ifdef S_IFDIR
1348 	  case S_IFDIR:
1349 		sprintf(p, "DIR: ");
1350 		p += strlen(p);
1351 		goto defprint;
1352 #endif
1353 
1354 #ifdef S_IFLNK
1355 	  case S_IFLNK:
1356 		sprintf(p, "LNK: ");
1357 		p += strlen(p);
1358 		goto defprint;
1359 #endif
1360 
1361 	  default:
1362 defprint:
1363 		if (sizeof st.st_size > sizeof (long))
1364 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
1365 		else
1366 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
1367 		sprintf(p, fmtstr,
1368 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1369 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1370 		break;
1371 	}
1372 
1373 printit:
1374 #ifdef LOG
1375 	if (logit)
1376 		syslog(LOG_DEBUG, "%s", buf);
1377 	else
1378 #endif
1379 		printf("%s\n", buf);
1380 }
1381 /*
1382 **  SHORTENSTRING -- return short version of a string
1383 **
1384 **	If the string is already short, just return it.  If it is too
1385 **	long, return the head and tail of the string.
1386 **
1387 **	Parameters:
1388 **		s -- the string to shorten.
1389 **		m -- the max length of the string.
1390 **
1391 **	Returns:
1392 **		Either s or a short version of s.
1393 */
1394 
1395 #ifndef MAXSHORTSTR
1396 # define MAXSHORTSTR	203
1397 #endif
1398 
1399 char *
1400 shortenstring(s, m)
1401 	register char *s;
1402 	int m;
1403 {
1404 	int l;
1405 	static char buf[MAXSHORTSTR + 1];
1406 
1407 	l = strlen(s);
1408 	if (l < m)
1409 		return s;
1410 	if (m > MAXSHORTSTR)
1411 		m = MAXSHORTSTR;
1412 	else if (m < 10)
1413 	{
1414 		if (m < 5)
1415 		{
1416 			strncpy(buf, s, m);
1417 			buf[m] = '\0';
1418 			return buf;
1419 		}
1420 		strncpy(buf, s, m - 3);
1421 		strcpy(buf + m - 3, "...");
1422 		return buf;
1423 	}
1424 	m = (m - 3) / 2;
1425 	strncpy(buf, s, m);
1426 	strcpy(buf + m, "...");
1427 	strcpy(buf + m + 3, s + l - m);
1428 	return buf;
1429 }
1430 /*
1431 **  GET_COLUMN  -- look up a Column in a line buffer
1432 **
1433 **	Parameters:
1434 **		line -- the raw text line to search.
1435 **		col -- the column number to fetch.
1436 **		delim -- the delimiter between columns.  If null,
1437 **			use white space.
1438 **		buf -- the output buffer.
1439 **
1440 **	Returns:
1441 **		buf if successful.
1442 **		NULL otherwise.
1443 */
1444 
1445 char *
1446 get_column(line, col, delim, buf)
1447 	char line[];
1448 	int col;
1449 	char delim;
1450 	char buf[];
1451 {
1452 	char *p;
1453 	char *begin, *end;
1454 	int i;
1455 	char delimbuf[3];
1456 
1457 	if (delim == '\0')
1458 		strcpy(delimbuf, "\t ");
1459 	else
1460 	{
1461 		delimbuf[0] = delim;
1462 		delimbuf[1] = '\0';
1463 	}
1464 
1465 	p = line;
1466 	if (*p == '\0')
1467 		return NULL;			/* line empty */
1468 	if (*p == delim && col == 0)
1469 		return NULL;			/* first column empty */
1470 
1471 	begin = line;
1472 
1473 	if (col == 0 && delim == '\0')
1474 	{
1475 		while (*begin && isspace(*begin))
1476 			begin++;
1477 	}
1478 
1479 	for (i = 0; i < col; i++)
1480 	{
1481 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1482 			return NULL;		/* no such column */
1483 		begin++;
1484 		if (delim == '\0')
1485 		{
1486 			while (*begin && isspace(*begin))
1487 				begin++;
1488 		}
1489 	}
1490 
1491 	end = strpbrk(begin, delimbuf);
1492 	if (end == NULL)
1493 	{
1494 		strcpy(buf, begin);
1495 	}
1496 	else
1497 	{
1498 		strncpy(buf, begin, end - begin);
1499 		buf[end - begin] = '\0';
1500 	}
1501 	return buf;
1502 }
1503 /*
1504 **  CLEANSTRCPY -- copy string keeping out bogus characters
1505 **
1506 **	Parameters:
1507 **		t -- "to" string.
1508 **		f -- "from" string.
1509 **		l -- length of space available in "to" string.
1510 **
1511 **	Returns:
1512 **		none.
1513 */
1514 
1515 void
1516 cleanstrcpy(t, f, l)
1517 	register char *t;
1518 	register char *f;
1519 	int l;
1520 {
1521 #ifdef LOG
1522 	/* check for newlines and log if necessary */
1523 	(void) denlstring(f);
1524 #endif
1525 
1526 	l--;
1527 	while (l > 0 && *f != '\0')
1528 	{
1529 		if (isascii(*f) &&
1530 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1531 		{
1532 			l--;
1533 			*t++ = *f;
1534 		}
1535 		f++;
1536 	}
1537 	*t = '\0';
1538 }
1539 /*
1540 **  DENLSTRING -- convert newlines in a string to spaces
1541 **
1542 **	Parameters:
1543 **		s -- the input string
1544 **
1545 **	Returns:
1546 **		A pointer to a version of the string with newlines
1547 **		mapped to spaces.  This should be copied.
1548 */
1549 
1550 char *
1551 denlstring(s)
1552 	char *s;
1553 {
1554 	register char *p;
1555 	int l;
1556 	static char *bp = NULL;
1557 	static int bl = 0;
1558 
1559 	if (strchr(s, '\n') == NULL)
1560 		return s;
1561 
1562 	l = strlen(s) + 1;
1563 	if (bl < l)
1564 	{
1565 		/* allocate more space */
1566 		if (bp != NULL)
1567 			free(bp);
1568 		bp = xalloc(l);
1569 		bl = l;
1570 	}
1571 	strcpy(bp, s);
1572 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
1573 		*p++ = ' ';
1574 
1575 #ifdef LOG
1576 	p = macvalue('_', CurEnv);
1577 	syslog(LOG_ALERT, "POSSIBLE ATTACK from %s: newline in string \"%s\"",
1578 		p == NULL ? "[UNKNOWN]" : p, bp);
1579 #endif
1580 
1581 	return bp;
1582 }
1583