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