xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision afc2c58a)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	6.8 (Berkeley) 02/26/93";
11 #endif /* not lint */
12 
13 # include <stdio.h>
14 # include <sys/types.h>
15 # include <sys/stat.h>
16 # include <sysexits.h>
17 # include <errno.h>
18 # include "sendmail.h"
19 /*
20 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
21 **
22 **	Runs through a string and strips off unquoted quote
23 **	characters and quote bits.  This is done in place.
24 **
25 **	Parameters:
26 **		s -- the string to strip.
27 **
28 **	Returns:
29 **		none.
30 **
31 **	Side Effects:
32 **		none.
33 **
34 **	Called By:
35 **		deliver
36 */
37 
38 stripquotes(s)
39 	char *s;
40 {
41 	register char *p;
42 	register char *q;
43 	register char c;
44 
45 	if (s == NULL)
46 		return;
47 
48 	p = q = s;
49 	do
50 	{
51 		c = *p++;
52 		if (c == '\\')
53 			c = *p++;
54 		else if (c == '"')
55 			continue;
56 		*q++ = c;
57 	} while (c != '\0');
58 }
59 /*
60 **  CAPITALIZE -- return a copy of a string, properly capitalized.
61 **
62 **	Parameters:
63 **		s -- the string to capitalize.
64 **
65 **	Returns:
66 **		a pointer to a properly capitalized string.
67 **
68 **	Side Effects:
69 **		none.
70 */
71 
72 char *
73 capitalize(s)
74 	register char *s;
75 {
76 	static char buf[50];
77 	register char *p;
78 
79 	p = buf;
80 
81 	for (;;)
82 	{
83 		while (!(isascii(*s) && isalpha(*s)) && *s != '\0')
84 			*p++ = *s++;
85 		if (*s == '\0')
86 			break;
87 		*p++ = toupper(*s);
88 		s++;
89 		while (isascii(*s) && isalpha(*s))
90 			*p++ = *s++;
91 	}
92 
93 	*p = '\0';
94 	return (buf);
95 }
96 /*
97 **  XALLOC -- Allocate memory and bitch wildly on failure.
98 **
99 **	THIS IS A CLUDGE.  This should be made to give a proper
100 **	error -- but after all, what can we do?
101 **
102 **	Parameters:
103 **		sz -- size of area to allocate.
104 **
105 **	Returns:
106 **		pointer to data region.
107 **
108 **	Side Effects:
109 **		Memory is allocated.
110 */
111 
112 char *
113 xalloc(sz)
114 	register int sz;
115 {
116 	register char *p;
117 
118 	p = malloc((unsigned) sz);
119 	if (p == NULL)
120 	{
121 		syserr("Out of memory!!");
122 		abort();
123 		/* exit(EX_UNAVAILABLE); */
124 	}
125 	return (p);
126 }
127 /*
128 **  COPYPLIST -- copy list of pointers.
129 **
130 **	This routine is the equivalent of newstr for lists of
131 **	pointers.
132 **
133 **	Parameters:
134 **		list -- list of pointers to copy.
135 **			Must be NULL terminated.
136 **		copycont -- if TRUE, copy the contents of the vector
137 **			(which must be a string) also.
138 **
139 **	Returns:
140 **		a copy of 'list'.
141 **
142 **	Side Effects:
143 **		none.
144 */
145 
146 char **
147 copyplist(list, copycont)
148 	char **list;
149 	bool copycont;
150 {
151 	register char **vp;
152 	register char **newvp;
153 
154 	for (vp = list; *vp != NULL; vp++)
155 		continue;
156 
157 	vp++;
158 
159 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
160 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
161 
162 	if (copycont)
163 	{
164 		for (vp = newvp; *vp != NULL; vp++)
165 			*vp = newstr(*vp);
166 	}
167 
168 	return (newvp);
169 }
170 /*
171 **  COPYQUEUE -- copy address queue.
172 **
173 **	This routine is the equivalent of newstr for address queues
174 **	addresses marked with QDONTSEND aren't copied
175 **
176 **	Parameters:
177 **		addr -- list of address structures to copy.
178 **
179 **	Returns:
180 **		a copy of 'addr'.
181 **
182 **	Side Effects:
183 **		none.
184 */
185 
186 ADDRESS *
187 copyqueue(addr)
188 	ADDRESS *addr;
189 {
190 	register ADDRESS *newaddr;
191 	ADDRESS *ret;
192 	register ADDRESS **tail = &ret;
193 
194 	while (addr != NULL)
195 	{
196 		if (!bitset(QDONTSEND, addr->q_flags))
197 		{
198 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
199 			STRUCTCOPY(*addr, *newaddr);
200 			*tail = newaddr;
201 			tail = &newaddr->q_next;
202 		}
203 		addr = addr->q_next;
204 	}
205 	*tail = NULL;
206 
207 	return ret;
208 }
209 /*
210 **  PRINTAV -- print argument vector.
211 **
212 **	Parameters:
213 **		av -- argument vector.
214 **
215 **	Returns:
216 **		none.
217 **
218 **	Side Effects:
219 **		prints av.
220 */
221 
222 printav(av)
223 	register char **av;
224 {
225 	while (*av != NULL)
226 	{
227 		if (tTd(0, 44))
228 			printf("\n\t%08x=", *av);
229 		else
230 			(void) putchar(' ');
231 		xputs(*av++);
232 	}
233 	(void) putchar('\n');
234 }
235 /*
236 **  LOWER -- turn letter into lower case.
237 **
238 **	Parameters:
239 **		c -- character to turn into lower case.
240 **
241 **	Returns:
242 **		c, in lower case.
243 **
244 **	Side Effects:
245 **		none.
246 */
247 
248 char
249 lower(c)
250 	register char c;
251 {
252 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
253 }
254 /*
255 **  XPUTS -- put string doing control escapes.
256 **
257 **	Parameters:
258 **		s -- string to put.
259 **
260 **	Returns:
261 **		none.
262 **
263 **	Side Effects:
264 **		output to stdout
265 */
266 
267 xputs(s)
268 	register char *s;
269 {
270 	register int c;
271 	register struct metamac *mp;
272 	extern struct metamac MetaMacros[];
273 
274 	if (s == NULL)
275 	{
276 		printf("<null>");
277 		return;
278 	}
279 	while ((c = (*s++ & 0377)) != '\0')
280 	{
281 		if (!isascii(c))
282 		{
283 			if (c == MATCHREPL || c == MACROEXPAND)
284 			{
285 				putchar('$');
286 				continue;
287 			}
288 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
289 			{
290 				if ((mp->metaval & 0377) == c)
291 				{
292 					printf("$%c", mp->metaname);
293 					break;
294 				}
295 			}
296 			if (mp->metaname != '\0')
297 				continue;
298 			(void) putchar('\\');
299 			c &= 0177;
300 		}
301 		if (isprint(c))
302 		{
303 			putchar(c);
304 			continue;
305 		}
306 
307 		/* wasn't a meta-macro -- find another way to print it */
308 		switch (c)
309 		{
310 		  case '\0':
311 			continue;
312 
313 		  case '\n':
314 			c = 'n';
315 			break;
316 
317 		  case '\r':
318 			c = 'r';
319 			break;
320 
321 		  case '\t':
322 			c = 't';
323 			break;
324 
325 		  default:
326 			(void) putchar('^');
327 			(void) putchar(c ^ 0100);
328 			continue;
329 		}
330 	}
331 	(void) fflush(stdout);
332 }
333 /*
334 **  MAKELOWER -- Translate a line into lower case
335 **
336 **	Parameters:
337 **		p -- the string to translate.  If NULL, return is
338 **			immediate.
339 **
340 **	Returns:
341 **		none.
342 **
343 **	Side Effects:
344 **		String pointed to by p is translated to lower case.
345 **
346 **	Called By:
347 **		parse
348 */
349 
350 makelower(p)
351 	register char *p;
352 {
353 	register char c;
354 
355 	if (p == NULL)
356 		return;
357 	for (; (c = *p) != '\0'; p++)
358 		if (isascii(c) && isupper(c))
359 			*p = tolower(c);
360 }
361 /*
362 **  BUILDFNAME -- build full name from gecos style entry.
363 **
364 **	This routine interprets the strange entry that would appear
365 **	in the GECOS field of the password file.
366 **
367 **	Parameters:
368 **		p -- name to build.
369 **		login -- the login name of this user (for &).
370 **		buf -- place to put the result.
371 **
372 **	Returns:
373 **		none.
374 **
375 **	Side Effects:
376 **		none.
377 */
378 
379 buildfname(gecos, login, buf)
380 	register char *gecos;
381 	char *login;
382 	char *buf;
383 {
384 	register char *p;
385 	register char *bp = buf;
386 	int l;
387 
388 	if (*gecos == '*')
389 		gecos++;
390 
391 	/* find length of final string */
392 	l = 0;
393 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
394 	{
395 		if (*p == '&')
396 			l += strlen(login);
397 		else
398 			l++;
399 	}
400 
401 	/* now fill in buf */
402 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
403 	{
404 		if (*p == '&')
405 		{
406 			(void) strcpy(bp, login);
407 			*bp = toupper(*bp);
408 			while (*bp != '\0')
409 				bp++;
410 		}
411 		else
412 			*bp++ = *p;
413 	}
414 	*bp = '\0';
415 }
416 /*
417 **  SAFEFILE -- return true if a file exists and is safe for a user.
418 **
419 **	Parameters:
420 **		fn -- filename to check.
421 **		uid -- uid to compare against.
422 **		mode -- mode bits that must match.
423 **
424 **	Returns:
425 **		0 if fn exists, is owned by uid, and matches mode.
426 **		An errno otherwise.  The actual errno is cleared.
427 **
428 **	Side Effects:
429 **		none.
430 */
431 
432 int
433 safefile(fn, uid, mode)
434 	char *fn;
435 	uid_t uid;
436 	int mode;
437 {
438 	struct stat stbuf;
439 
440 	if (stat(fn, &stbuf) < 0)
441 	{
442 		int ret = errno;
443 
444 		errno = 0;
445 		return ret;
446 	}
447 	if (stbuf.st_uid == uid && (stbuf.st_mode & mode) == mode)
448 		return 0;
449 	return EPERM;
450 }
451 /*
452 **  FIXCRLF -- fix <CR><LF> in line.
453 **
454 **	Looks for the <CR><LF> combination and turns it into the
455 **	UNIX canonical <NL> character.  It only takes one line,
456 **	i.e., it is assumed that the first <NL> found is the end
457 **	of the line.
458 **
459 **	Parameters:
460 **		line -- the line to fix.
461 **		stripnl -- if true, strip the newline also.
462 **
463 **	Returns:
464 **		none.
465 **
466 **	Side Effects:
467 **		line is changed in place.
468 */
469 
470 fixcrlf(line, stripnl)
471 	char *line;
472 	bool stripnl;
473 {
474 	register char *p;
475 
476 	p = strchr(line, '\n');
477 	if (p == NULL)
478 		return;
479 	if (p > line && p[-1] == '\r')
480 		p--;
481 	if (!stripnl)
482 		*p++ = '\n';
483 	*p = '\0';
484 }
485 /*
486 **  DFOPEN -- determined file open
487 **
488 **	This routine has the semantics of fopen, except that it will
489 **	keep trying a few times to make this happen.  The idea is that
490 **	on very loaded systems, we may run out of resources (inodes,
491 **	whatever), so this tries to get around it.
492 */
493 
494 FILE *
495 dfopen(filename, mode)
496 	char *filename;
497 	char *mode;
498 {
499 	register int tries;
500 	register FILE *fp;
501 
502 	for (tries = 0; tries < 10; tries++)
503 	{
504 		sleep((unsigned) (10 * tries));
505 		errno = 0;
506 		fp = fopen(filename, mode);
507 		if (fp != NULL)
508 			break;
509 		if (errno != ENFILE && errno != EINTR)
510 			break;
511 	}
512 	if (fp != NULL)
513 	{
514 #ifdef FLOCK
515 		int locktype;
516 
517 		/* lock the file to avoid accidental conflicts */
518 		if (*mode == 'w' || *mode == 'a')
519 			locktype = LOCK_EX;
520 		else
521 			locktype = LOCK_SH;
522 		(void) flock(fileno(fp), locktype);
523 #endif
524 		errno = 0;
525 	}
526 	return (fp);
527 }
528 /*
529 **  PUTLINE -- put a line like fputs obeying SMTP conventions
530 **
531 **	This routine always guarantees outputing a newline (or CRLF,
532 **	as appropriate) at the end of the string.
533 **
534 **	Parameters:
535 **		l -- line to put.
536 **		fp -- file to put it onto.
537 **		m -- the mailer used to control output.
538 **
539 **	Returns:
540 **		none
541 **
542 **	Side Effects:
543 **		output of l to fp.
544 */
545 
546 putline(l, fp, m)
547 	register char *l;
548 	FILE *fp;
549 	MAILER *m;
550 {
551 	register char *p;
552 	register char svchar;
553 
554 	/* strip out 0200 bits -- these can look like TELNET protocol */
555 	if (bitnset(M_7BITS, m->m_flags))
556 	{
557 		for (p = l; svchar = *p; ++p)
558 			if (svchar & 0200)
559 				*p = svchar &~ 0200;
560 	}
561 
562 	do
563 	{
564 		/* find the end of the line */
565 		p = strchr(l, '\n');
566 		if (p == NULL)
567 			p = &l[strlen(l)];
568 
569 		/* check for line overflow */
570 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
571 		{
572 			register char *q = &l[m->m_linelimit - 1];
573 
574 			svchar = *q;
575 			*q = '\0';
576 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
577 				(void) putc('.', fp);
578 			fputs(l, fp);
579 			(void) putc('!', fp);
580 			fputs(m->m_eol, fp);
581 			*q = svchar;
582 			l = q;
583 		}
584 
585 		/* output last part */
586 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
587 			(void) putc('.', fp);
588 		for ( ; l < p; ++l)
589 			(void) putc(*l, fp);
590 		fputs(m->m_eol, fp);
591 		if (*l == '\n')
592 			++l;
593 	} while (l[0] != '\0');
594 }
595 /*
596 **  XUNLINK -- unlink a file, doing logging as appropriate.
597 **
598 **	Parameters:
599 **		f -- name of file to unlink.
600 **
601 **	Returns:
602 **		none.
603 **
604 **	Side Effects:
605 **		f is unlinked.
606 */
607 
608 xunlink(f)
609 	char *f;
610 {
611 	register int i;
612 
613 # ifdef LOG
614 	if (LogLevel > 98)
615 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
616 # endif /* LOG */
617 
618 	i = unlink(f);
619 # ifdef LOG
620 	if (i < 0 && LogLevel > 97)
621 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
622 # endif /* LOG */
623 }
624 /*
625 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
626 **
627 **	Parameters:
628 **		buf -- place to put the input line.
629 **		siz -- size of buf.
630 **		fp -- file to read from.
631 **		timeout -- the timeout before error occurs.
632 **
633 **	Returns:
634 **		NULL on error (including timeout).  This will also leave
635 **			buf containing a null string.
636 **		buf otherwise.
637 **
638 **	Side Effects:
639 **		none.
640 */
641 
642 static jmp_buf	CtxReadTimeout;
643 
644 char *
645 sfgets(buf, siz, fp, timeout)
646 	char *buf;
647 	int siz;
648 	FILE *fp;
649 	time_t timeout;
650 {
651 	register EVENT *ev = NULL;
652 	register char *p;
653 	static int readtimeout();
654 
655 	/* set the timeout */
656 	if (timeout != 0)
657 	{
658 		if (setjmp(CtxReadTimeout) != 0)
659 		{
660 # ifdef LOG
661 			syslog(LOG_NOTICE,
662 			    "timeout waiting for input from %s\n",
663 			    CurHostName? CurHostName: "local");
664 # endif
665 			errno = 0;
666 			usrerr("451 timeout waiting for input");
667 			buf[0] = '\0';
668 			return (NULL);
669 		}
670 		ev = setevent(timeout, readtimeout, 0);
671 	}
672 
673 	/* try to read */
674 	p = NULL;
675 	while (p == NULL && !feof(fp) && !ferror(fp))
676 	{
677 		errno = 0;
678 		p = fgets(buf, siz, fp);
679 		if (errno == EINTR)
680 			clearerr(fp);
681 	}
682 
683 	/* clear the event if it has not sprung */
684 	clrevent(ev);
685 
686 	/* clean up the books and exit */
687 	LineNumber++;
688 	if (p == NULL)
689 	{
690 		buf[0] = '\0';
691 		return (NULL);
692 	}
693 	if (!EightBit)
694 		for (p = buf; *p != '\0'; p++)
695 			*p &= ~0200;
696 	return (buf);
697 }
698 
699 static
700 readtimeout()
701 {
702 	longjmp(CtxReadTimeout, 1);
703 }
704 /*
705 **  FGETFOLDED -- like fgets, but know about folded lines.
706 **
707 **	Parameters:
708 **		buf -- place to put result.
709 **		n -- bytes available.
710 **		f -- file to read from.
711 **
712 **	Returns:
713 **		input line(s) on success, NULL on error or EOF.
714 **		This will normally be buf -- unless the line is too
715 **			long, when it will be xalloc()ed.
716 **
717 **	Side Effects:
718 **		buf gets lines from f, with continuation lines (lines
719 **		with leading white space) appended.  CRLF's are mapped
720 **		into single newlines.  Any trailing NL is stripped.
721 */
722 
723 char *
724 fgetfolded(buf, n, f)
725 	char *buf;
726 	register int n;
727 	FILE *f;
728 {
729 	register char *p = buf;
730 	char *bp = buf;
731 	register int i;
732 
733 	n--;
734 	while ((i = getc(f)) != EOF)
735 	{
736 		if (i == '\r')
737 		{
738 			i = getc(f);
739 			if (i != '\n')
740 			{
741 				if (i != EOF)
742 					(void) ungetc(i, f);
743 				i = '\r';
744 			}
745 		}
746 		if (--n <= 0)
747 		{
748 			/* allocate new space */
749 			char *nbp;
750 			int nn;
751 
752 			nn = (p - bp);
753 			if (nn < MEMCHUNKSIZE)
754 				nn *= 2;
755 			else
756 				nn += MEMCHUNKSIZE;
757 			nbp = xalloc(nn);
758 			bcopy(bp, nbp, p - bp);
759 			p = &nbp[p - bp];
760 			if (bp != buf)
761 				free(bp);
762 			bp = nbp;
763 			n = nn - (p - bp);
764 		}
765 		*p++ = i;
766 		if (i == '\n')
767 		{
768 			LineNumber++;
769 			i = getc(f);
770 			if (i != EOF)
771 				(void) ungetc(i, f);
772 			if (i != ' ' && i != '\t')
773 				break;
774 		}
775 	}
776 	if (p == bp)
777 		return (NULL);
778 	*--p = '\0';
779 	return (bp);
780 }
781 /*
782 **  CURTIME -- return current time.
783 **
784 **	Parameters:
785 **		none.
786 **
787 **	Returns:
788 **		the current time.
789 **
790 **	Side Effects:
791 **		none.
792 */
793 
794 time_t
795 curtime()
796 {
797 	auto time_t t;
798 
799 	(void) time(&t);
800 	return (t);
801 }
802 /*
803 **  ATOBOOL -- convert a string representation to boolean.
804 **
805 **	Defaults to "TRUE"
806 **
807 **	Parameters:
808 **		s -- string to convert.  Takes "tTyY" as true,
809 **			others as false.
810 **
811 **	Returns:
812 **		A boolean representation of the string.
813 **
814 **	Side Effects:
815 **		none.
816 */
817 
818 bool
819 atobool(s)
820 	register char *s;
821 {
822 	if (*s == '\0' || strchr("tTyY", *s) != NULL)
823 		return (TRUE);
824 	return (FALSE);
825 }
826 /*
827 **  ATOOCT -- convert a string representation to octal.
828 **
829 **	Parameters:
830 **		s -- string to convert.
831 **
832 **	Returns:
833 **		An integer representing the string interpreted as an
834 **		octal number.
835 **
836 **	Side Effects:
837 **		none.
838 */
839 
840 atooct(s)
841 	register char *s;
842 {
843 	register int i = 0;
844 
845 	while (*s >= '0' && *s <= '7')
846 		i = (i << 3) | (*s++ - '0');
847 	return (i);
848 }
849 /*
850 **  WAITFOR -- wait for a particular process id.
851 **
852 **	Parameters:
853 **		pid -- process id to wait for.
854 **
855 **	Returns:
856 **		status of pid.
857 **		-1 if pid never shows up.
858 **
859 **	Side Effects:
860 **		none.
861 */
862 
863 waitfor(pid)
864 	int pid;
865 {
866 	auto int st;
867 	int i;
868 
869 	do
870 	{
871 		errno = 0;
872 		i = wait(&st);
873 	} while ((i >= 0 || errno == EINTR) && i != pid);
874 	if (i < 0)
875 		st = -1;
876 	return (st);
877 }
878 /*
879 **  BITINTERSECT -- tell if two bitmaps intersect
880 **
881 **	Parameters:
882 **		a, b -- the bitmaps in question
883 **
884 **	Returns:
885 **		TRUE if they have a non-null intersection
886 **		FALSE otherwise
887 **
888 **	Side Effects:
889 **		none.
890 */
891 
892 bool
893 bitintersect(a, b)
894 	BITMAP a;
895 	BITMAP b;
896 {
897 	int i;
898 
899 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
900 		if ((a[i] & b[i]) != 0)
901 			return (TRUE);
902 	return (FALSE);
903 }
904 /*
905 **  BITZEROP -- tell if a bitmap is all zero
906 **
907 **	Parameters:
908 **		map -- the bit map to check
909 **
910 **	Returns:
911 **		TRUE if map is all zero.
912 **		FALSE if there are any bits set in map.
913 **
914 **	Side Effects:
915 **		none.
916 */
917 
918 bool
919 bitzerop(map)
920 	BITMAP map;
921 {
922 	int i;
923 
924 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
925 		if (map[i] != 0)
926 			return (FALSE);
927 	return (TRUE);
928 }
929 /*
930 **  TRANSIENTERROR -- tell if an error code indicates a transient failure
931 **
932 **	This looks at an errno value and tells if this is likely to
933 **	go away if retried later.
934 **
935 **	Parameters:
936 **		err -- the errno code to classify.
937 **
938 **	Returns:
939 **		TRUE if this is probably transient.
940 **		FALSE otherwise.
941 */
942 
943 bool
944 transienterror(err)
945 	int err;
946 {
947 	switch (err)
948 	{
949 	  case EIO:			/* I/O error */
950 	  case ENXIO:			/* Device not configured */
951 	  case EAGAIN:			/* Resource temporarily unavailable */
952 	  case ENOMEM:			/* Cannot allocate memory */
953 	  case ENODEV:			/* Operation not supported by device */
954 	  case ENFILE:			/* Too many open files in system */
955 	  case EMFILE:			/* Too many open files */
956 	  case ENOSPC:			/* No space left on device */
957 #ifdef ETIMEDOUT
958 	  case ETIMEDOUT:		/* Connection timed out */
959 #endif
960 #ifdef ESTALE
961 	  case ESTALE:			/* Stale NFS file handle */
962 #endif
963 #ifdef ENETDOWN
964 	  case ENETDOWN:		/* Network is down */
965 #endif
966 #ifdef ENETUNREACH
967 	  case ENETUNREACH:		/* Network is unreachable */
968 #endif
969 #ifdef ENETRESET
970 	  case ENETRESET:		/* Network dropped connection on reset */
971 #endif
972 #ifdef ECONNABORTED
973 	  case ECONNABORTED:		/* Software caused connection abort */
974 #endif
975 #ifdef ECONNRESET
976 	  case ECONNRESET:		/* Connection reset by peer */
977 #endif
978 #ifdef ENOBUFS
979 	  case ENOBUFS:			/* No buffer space available */
980 #endif
981 #ifdef ESHUTDOWN
982 	  case ESHUTDOWN:		/* Can't send after socket shutdown */
983 #endif
984 #ifdef ECONNREFUSED
985 	  case ECONNREFUSED:		/* Connection refused */
986 #endif
987 #ifdef EHOSTDOWN
988 	  case EHOSTDOWN:		/* Host is down */
989 #endif
990 #ifdef EHOSTUNREACH
991 	  case EHOSTUNREACH:		/* No route to host */
992 #endif
993 #ifdef EDQUOT
994 	  case EDQUOT:			/* Disc quota exceeded */
995 #endif
996 #ifdef EPROCLIM
997 	  case EPROCLIM:		/* Too many processes */
998 #endif
999 #ifdef EUSERS
1000 	  case EUSERS:			/* Too many users */
1001 #endif
1002 #ifdef EDEADLK
1003 	  case EDEADLK:			/* Resource deadlock avoided */
1004 #endif
1005 		return TRUE;
1006 	}
1007 
1008 	/* nope, must be permanent */
1009 	return FALSE;
1010 }
1011