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