1 /*
2 * Copyright (c) 1998-2007, 2009 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14 #include <sendmail.h>
15
16 SM_RCSID("@(#)$Id: util.c,v 8.416 2009/12/18 17:05:26 ca Exp $")
17
18 #include <sm/sendmail.h>
19 #include <sysexits.h>
20 #include <sm/xtrap.h>
21 #include <sys/types.h>
22 #include <sys/mkdev.h>
23
24 /*
25 ** NEWSTR -- Create a copy of a C string
26 **
27 ** Parameters:
28 ** s -- the string to copy.
29 **
30 ** Returns:
31 ** pointer to newly allocated string.
32 */
33
34 char *
35 newstr(s)
36 const char *s;
37 {
38 size_t l;
39 char *n;
40
41 l = strlen(s);
42 SM_ASSERT(l + 1 > l);
43 n = xalloc(l + 1);
44 sm_strlcpy(n, s, l + 1);
45 return n;
46 }
47
48 /*
49 ** ADDQUOTES -- Adds quotes & quote bits to a string.
50 **
51 ** Runs through a string and adds backslashes and quote bits.
52 **
53 ** Parameters:
54 ** s -- the string to modify.
55 ** rpool -- resource pool from which to allocate result
56 **
57 ** Returns:
58 ** pointer to quoted string.
59 */
60
61 char *
62 addquotes(s, rpool)
63 char *s;
64 SM_RPOOL_T *rpool;
65 {
66 int len = 0;
67 char c;
68 char *p = s, *q, *r;
69
70 if (s == NULL)
71 return NULL;
72
73 /* Find length of quoted string */
74 while ((c = *p++) != '\0')
75 {
76 len++;
77 if (c == '\\' || c == '"')
78 len++;
79 }
80
81 q = r = sm_rpool_malloc_x(rpool, len + 3);
82 p = s;
83
84 /* add leading quote */
85 *q++ = '"';
86 while ((c = *p++) != '\0')
87 {
88 /* quote \ or " */
89 if (c == '\\' || c == '"')
90 *q++ = '\\';
91 *q++ = c;
92 }
93 *q++ = '"';
94 *q = '\0';
95 return r;
96 }
97
98 /*
99 ** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
100 ** the following character is alpha-numerical.
101 **
102 ** This is done in place.
103 **
104 ** Parameters:
105 ** s -- the string to strip.
106 **
107 ** Returns:
108 ** none.
109 */
110
111 void
112 stripbackslash(s)
113 char *s;
114 {
115 char *p, *q, c;
116
117 if (s == NULL || *s == '\0')
118 return;
119 p = q = s;
120 while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
121 p++;
122 do
123 {
124 c = *q++ = *p++;
125 } while (c != '\0');
126 }
127
128 /*
129 ** RFC822_STRING -- Checks string for proper RFC822 string quoting.
130 **
131 ** Runs through a string and verifies RFC822 special characters
132 ** are only found inside comments, quoted strings, or backslash
133 ** escaped. Also verified balanced quotes and parenthesis.
134 **
135 ** Parameters:
136 ** s -- the string to modify.
137 **
138 ** Returns:
139 ** true iff the string is RFC822 compliant, false otherwise.
140 */
141
142 bool
143 rfc822_string(s)
144 char *s;
145 {
146 bool quoted = false;
147 int commentlev = 0;
148 char *c = s;
149
150 if (s == NULL)
151 return false;
152
153 while (*c != '\0')
154 {
155 /* escaped character */
156 if (*c == '\\')
157 {
158 c++;
159 if (*c == '\0')
160 return false;
161 }
162 else if (commentlev == 0 && *c == '"')
163 quoted = !quoted;
164 else if (!quoted)
165 {
166 if (*c == ')')
167 {
168 /* unbalanced ')' */
169 if (commentlev == 0)
170 return false;
171 else
172 commentlev--;
173 }
174 else if (*c == '(')
175 commentlev++;
176 else if (commentlev == 0 &&
177 strchr(MustQuoteChars, *c) != NULL)
178 return false;
179 }
180 c++;
181 }
182
183 /* unbalanced '"' or '(' */
184 return !quoted && commentlev == 0;
185 }
186
187 /*
188 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
189 **
190 ** Arbitrarily shorten (in place) an RFC822 string and rebalance
191 ** comments and quotes.
192 **
193 ** Parameters:
194 ** string -- the string to shorten
195 ** length -- the maximum size, 0 if no maximum
196 **
197 ** Returns:
198 ** true if string is changed, false otherwise
199 **
200 ** Side Effects:
201 ** Changes string in place, possibly resulting
202 ** in a shorter string.
203 */
204
205 bool
206 shorten_rfc822_string(string, length)
207 char *string;
208 size_t length;
209 {
210 bool backslash = false;
211 bool modified = false;
212 bool quoted = false;
213 size_t slen;
214 int parencount = 0;
215 char *ptr = string;
216
217 /*
218 ** If have to rebalance an already short enough string,
219 ** need to do it within allocated space.
220 */
221
222 slen = strlen(string);
223 if (length == 0 || slen < length)
224 length = slen;
225
226 while (*ptr != '\0')
227 {
228 if (backslash)
229 {
230 backslash = false;
231 goto increment;
232 }
233
234 if (*ptr == '\\')
235 backslash = true;
236 else if (*ptr == '(')
237 {
238 if (!quoted)
239 parencount++;
240 }
241 else if (*ptr == ')')
242 {
243 if (--parencount < 0)
244 parencount = 0;
245 }
246
247 /* Inside a comment, quotes don't matter */
248 if (parencount <= 0 && *ptr == '"')
249 quoted = !quoted;
250
251 increment:
252 /* Check for sufficient space for next character */
253 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
254 parencount +
255 (quoted ? 1 : 0)))
256 {
257 /* Not enough, backtrack */
258 if (*ptr == '\\')
259 backslash = false;
260 else if (*ptr == '(' && !quoted)
261 parencount--;
262 else if (*ptr == '"' && parencount == 0)
263 quoted = false;
264 break;
265 }
266 ptr++;
267 }
268
269 /* Rebalance */
270 while (parencount-- > 0)
271 {
272 if (*ptr != ')')
273 {
274 modified = true;
275 *ptr = ')';
276 }
277 ptr++;
278 }
279 if (quoted)
280 {
281 if (*ptr != '"')
282 {
283 modified = true;
284 *ptr = '"';
285 }
286 ptr++;
287 }
288 if (*ptr != '\0')
289 {
290 modified = true;
291 *ptr = '\0';
292 }
293 return modified;
294 }
295
296 /*
297 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string
298 **
299 ** Find an unquoted, non-commented character in an RFC822
300 ** string and return a pointer to its location in the
301 ** string.
302 **
303 ** Parameters:
304 ** string -- the string to search
305 ** character -- the character to find
306 **
307 ** Returns:
308 ** pointer to the character, or
309 ** a pointer to the end of the line if character is not found
310 */
311
312 char *
313 find_character(string, character)
314 char *string;
315 int character;
316 {
317 bool backslash = false;
318 bool quoted = false;
319 int parencount = 0;
320
321 while (string != NULL && *string != '\0')
322 {
323 if (backslash)
324 {
325 backslash = false;
326 if (!quoted && character == '\\' && *string == '\\')
327 break;
328 string++;
329 continue;
330 }
331 switch (*string)
332 {
333 case '\\':
334 backslash = true;
335 break;
336
337 case '(':
338 if (!quoted)
339 parencount++;
340 break;
341
342 case ')':
343 if (--parencount < 0)
344 parencount = 0;
345 break;
346 }
347
348 /* Inside a comment, nothing matters */
349 if (parencount > 0)
350 {
351 string++;
352 continue;
353 }
354
355 if (*string == '"')
356 quoted = !quoted;
357 else if (*string == character && !quoted)
358 break;
359 string++;
360 }
361
362 /* Return pointer to the character */
363 return string;
364 }
365
366 /*
367 ** CHECK_BODYTYPE -- check bodytype parameter
368 **
369 ** Parameters:
370 ** bodytype -- bodytype parameter
371 **
372 ** Returns:
373 ** BODYTYPE_* according to parameter
374 **
375 */
376
377 int
378 check_bodytype(bodytype)
379 char *bodytype;
380 {
381 /* check body type for legality */
382 if (bodytype == NULL)
383 return BODYTYPE_NONE;
384 if (sm_strcasecmp(bodytype, "7BIT") == 0)
385 return BODYTYPE_7BIT;
386 if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
387 return BODYTYPE_8BITMIME;
388 return BODYTYPE_ILLEGAL;
389 }
390
391 /*
392 ** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
393 **
394 ** Parameters:
395 ** str -- string to truncate
396 ** len -- maximum length (including '\0') (0 for unlimited)
397 ** delim -- delimiter character
398 **
399 ** Returns:
400 ** None.
401 */
402
403 void
404 truncate_at_delim(str, len, delim)
405 char *str;
406 size_t len;
407 int delim;
408 {
409 char *p;
410
411 if (str == NULL || len == 0 || strlen(str) < len)
412 return;
413
414 *(str + len - 1) = '\0';
415 while ((p = strrchr(str, delim)) != NULL)
416 {
417 *p = '\0';
418 if (p - str + 4 < len)
419 {
420 *p++ = (char) delim;
421 *p = '\0';
422 (void) sm_strlcat(str, "...", len);
423 return;
424 }
425 }
426
427 /* Couldn't find a place to append "..." */
428 if (len > 3)
429 (void) sm_strlcpy(str, "...", len);
430 else
431 str[0] = '\0';
432 }
433
434 /*
435 ** XALLOC -- Allocate memory, raise an exception on error
436 **
437 ** Parameters:
438 ** sz -- size of area to allocate.
439 **
440 ** Returns:
441 ** pointer to data region.
442 **
443 ** Exceptions:
444 ** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
445 **
446 ** Side Effects:
447 ** Memory is allocated.
448 */
449
450 char *
451 #if SM_HEAP_CHECK
452 xalloc_tagged(sz, file, line)
453 register int sz;
454 char *file;
455 int line;
456 #else /* SM_HEAP_CHECK */
457 xalloc(sz)
458 register int sz;
459 #endif /* SM_HEAP_CHECK */
460 {
461 register char *p;
462
463 SM_REQUIRE(sz >= 0);
464
465 /* some systems can't handle size zero mallocs */
466 if (sz <= 0)
467 sz = 1;
468
469 /* scaffolding for testing error handling code */
470 sm_xtrap_raise_x(&SmHeapOutOfMemory);
471
472 p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
473 if (p == NULL)
474 {
475 sm_exc_raise_x(&SmHeapOutOfMemory);
476 }
477 return p;
478 }
479
480 /*
481 ** COPYPLIST -- copy list of pointers.
482 **
483 ** This routine is the equivalent of strdup for lists of
484 ** pointers.
485 **
486 ** Parameters:
487 ** list -- list of pointers to copy.
488 ** Must be NULL terminated.
489 ** copycont -- if true, copy the contents of the vector
490 ** (which must be a string) also.
491 ** rpool -- resource pool from which to allocate storage,
492 ** or NULL
493 **
494 ** Returns:
495 ** a copy of 'list'.
496 */
497
498 char **
499 copyplist(list, copycont, rpool)
500 char **list;
501 bool copycont;
502 SM_RPOOL_T *rpool;
503 {
504 register char **vp;
505 register char **newvp;
506
507 for (vp = list; *vp != NULL; vp++)
508 continue;
509
510 vp++;
511
512 newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof(*vp));
513 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
514
515 if (copycont)
516 {
517 for (vp = newvp; *vp != NULL; vp++)
518 *vp = sm_rpool_strdup_x(rpool, *vp);
519 }
520
521 return newvp;
522 }
523
524 /*
525 ** COPYQUEUE -- copy address queue.
526 **
527 ** This routine is the equivalent of strdup for address queues;
528 ** addresses marked as QS_IS_DEAD() aren't copied
529 **
530 ** Parameters:
531 ** addr -- list of address structures to copy.
532 ** rpool -- resource pool from which to allocate storage
533 **
534 ** Returns:
535 ** a copy of 'addr'.
536 */
537
538 ADDRESS *
539 copyqueue(addr, rpool)
540 ADDRESS *addr;
541 SM_RPOOL_T *rpool;
542 {
543 register ADDRESS *newaddr;
544 ADDRESS *ret;
545 register ADDRESS **tail = &ret;
546
547 while (addr != NULL)
548 {
549 if (!QS_IS_DEAD(addr->q_state))
550 {
551 newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
552 sizeof(*newaddr));
553 STRUCTCOPY(*addr, *newaddr);
554 *tail = newaddr;
555 tail = &newaddr->q_next;
556 }
557 addr = addr->q_next;
558 }
559 *tail = NULL;
560
561 return ret;
562 }
563
564 /*
565 ** LOG_SENDMAIL_PID -- record sendmail pid and command line.
566 **
567 ** Parameters:
568 ** e -- the current envelope.
569 **
570 ** Returns:
571 ** none.
572 **
573 ** Side Effects:
574 ** writes pidfile, logs command line.
575 ** keeps file open and locked to prevent overwrite of active file
576 */
577
578 static SM_FILE_T *Pidf = NULL;
579
580 void
581 log_sendmail_pid(e)
582 ENVELOPE *e;
583 {
584 long sff;
585 char pidpath[MAXPATHLEN];
586 extern char *CommandLineArgs;
587
588 /* write the pid to the log file for posterity */
589 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
590 if (TrustedUid != 0 && RealUid == TrustedUid)
591 sff |= SFF_OPENASROOT;
592 expand(PidFile, pidpath, sizeof(pidpath), e);
593 Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
594 if (Pidf == NULL)
595 {
596 if (errno == EWOULDBLOCK)
597 sm_syslog(LOG_ERR, NOQID,
598 "unable to write pid to %s: file in use by another process",
599 pidpath);
600 else
601 sm_syslog(LOG_ERR, NOQID,
602 "unable to write pid to %s: %s",
603 pidpath, sm_errstring(errno));
604 }
605 else
606 {
607 PidFilePid = getpid();
608
609 /* write the process id on line 1 */
610 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
611 (long) PidFilePid);
612
613 /* line 2 contains all command line flags */
614 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
615 CommandLineArgs);
616
617 /* flush */
618 (void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
619
620 /*
621 ** Leave pid file open until process ends
622 ** so it's not overwritten by another
623 ** process.
624 */
625 }
626 if (LogLevel > 9)
627 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
628 }
629
630 /*
631 ** CLOSE_SENDMAIL_PID -- close sendmail pid file
632 **
633 ** Parameters:
634 ** none.
635 **
636 ** Returns:
637 ** none.
638 */
639
640 void
641 close_sendmail_pid()
642 {
643 if (Pidf == NULL)
644 return;
645
646 (void) sm_io_close(Pidf, SM_TIME_DEFAULT);
647 Pidf = NULL;
648 }
649
650 /*
651 ** SET_DELIVERY_MODE -- set and record the delivery mode
652 **
653 ** Parameters:
654 ** mode -- delivery mode
655 ** e -- the current envelope.
656 **
657 ** Returns:
658 ** none.
659 **
660 ** Side Effects:
661 ** sets {deliveryMode} macro
662 */
663
664 void
665 set_delivery_mode(mode, e)
666 int mode;
667 ENVELOPE *e;
668 {
669 char buf[2];
670
671 e->e_sendmode = (char) mode;
672 buf[0] = (char) mode;
673 buf[1] = '\0';
674 macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
675 }
676
677 /*
678 ** SET_OP_MODE -- set and record the op mode
679 **
680 ** Parameters:
681 ** mode -- op mode
682 ** e -- the current envelope.
683 **
684 ** Returns:
685 ** none.
686 **
687 ** Side Effects:
688 ** sets {opMode} macro
689 */
690
691 void
692 set_op_mode(mode)
693 int mode;
694 {
695 char buf[2];
696 extern ENVELOPE BlankEnvelope;
697
698 OpMode = (char) mode;
699 buf[0] = (char) mode;
700 buf[1] = '\0';
701 macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
702 }
703
704 /*
705 ** PRINTAV -- print argument vector.
706 **
707 ** Parameters:
708 ** fp -- output file pointer.
709 ** av -- argument vector.
710 **
711 ** Returns:
712 ** none.
713 **
714 ** Side Effects:
715 ** prints av.
716 */
717
718 void
719 printav(fp, av)
720 SM_FILE_T *fp;
721 char **av;
722 {
723 while (*av != NULL)
724 {
725 if (tTd(0, 44))
726 sm_dprintf("\n\t%08lx=", (unsigned long) *av);
727 else
728 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
729 if (tTd(0, 99))
730 sm_dprintf("%s", str2prt(*av++));
731 else
732 xputs(fp, *av++);
733 }
734 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
735 }
736
737 /*
738 ** XPUTS -- put string doing control escapes.
739 **
740 ** Parameters:
741 ** fp -- output file pointer.
742 ** s -- string to put.
743 **
744 ** Returns:
745 ** none.
746 **
747 ** Side Effects:
748 ** output to stdout
749 */
750
751 void
752 xputs(fp, s)
753 SM_FILE_T *fp;
754 const char *s;
755 {
756 int c;
757 struct metamac *mp;
758 bool shiftout = false;
759 extern struct metamac MetaMacros[];
760 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
761 "@(#)$Debug: ANSI - enable reverse video in debug output $");
762
763 /*
764 ** TermEscape is set here, rather than in main(),
765 ** because ANSI mode can be turned on or off at any time
766 ** if we are in -bt rule testing mode.
767 */
768
769 if (sm_debug_unknown(&DebugANSI))
770 {
771 if (sm_debug_active(&DebugANSI, 1))
772 {
773 TermEscape.te_rv_on = "\033[7m";
774 TermEscape.te_normal = "\033[0m";
775 }
776 else
777 {
778 TermEscape.te_rv_on = "";
779 TermEscape.te_normal = "";
780 }
781 }
782
783 if (s == NULL)
784 {
785 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
786 TermEscape.te_rv_on, TermEscape.te_normal);
787 return;
788 }
789 while ((c = (*s++ & 0377)) != '\0')
790 {
791 if (shiftout)
792 {
793 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
794 TermEscape.te_normal);
795 shiftout = false;
796 }
797 if (!isascii(c) && !tTd(84, 1))
798 {
799 if (c == MATCHREPL)
800 {
801 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
802 "%s$",
803 TermEscape.te_rv_on);
804 shiftout = true;
805 if (*s == '\0')
806 continue;
807 c = *s++ & 0377;
808 goto printchar;
809 }
810 if (c == MACROEXPAND || c == MACRODEXPAND)
811 {
812 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
813 "%s$",
814 TermEscape.te_rv_on);
815 if (c == MACRODEXPAND)
816 (void) sm_io_putc(fp,
817 SM_TIME_DEFAULT, '&');
818 shiftout = true;
819 if (*s == '\0')
820 continue;
821 if (strchr("=~&?", *s) != NULL)
822 (void) sm_io_putc(fp,
823 SM_TIME_DEFAULT,
824 *s++);
825 if (bitset(0200, *s))
826 (void) sm_io_fprintf(fp,
827 SM_TIME_DEFAULT,
828 "{%s}",
829 macname(bitidx(*s++)));
830 else
831 (void) sm_io_fprintf(fp,
832 SM_TIME_DEFAULT,
833 "%c",
834 *s++);
835 continue;
836 }
837 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
838 {
839 if (bitidx(mp->metaval) == c)
840 {
841 (void) sm_io_fprintf(fp,
842 SM_TIME_DEFAULT,
843 "%s$%c",
844 TermEscape.te_rv_on,
845 mp->metaname);
846 shiftout = true;
847 break;
848 }
849 }
850 if (c == MATCHCLASS || c == MATCHNCLASS)
851 {
852 if (bitset(0200, *s))
853 (void) sm_io_fprintf(fp,
854 SM_TIME_DEFAULT,
855 "{%s}",
856 macname(bitidx(*s++)));
857 else if (*s != '\0')
858 (void) sm_io_fprintf(fp,
859 SM_TIME_DEFAULT,
860 "%c",
861 *s++);
862 }
863 if (mp->metaname != '\0')
864 continue;
865
866 /* unrecognized meta character */
867 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
868 TermEscape.te_rv_on);
869 shiftout = true;
870 c &= 0177;
871 }
872 printchar:
873 if (isascii(c) && isprint(c))
874 {
875 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
876 continue;
877 }
878
879 /* wasn't a meta-macro -- find another way to print it */
880 switch (c)
881 {
882 case '\n':
883 c = 'n';
884 break;
885
886 case '\r':
887 c = 'r';
888 break;
889
890 case '\t':
891 c = 't';
892 break;
893 }
894 if (!shiftout)
895 {
896 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
897 TermEscape.te_rv_on);
898 shiftout = true;
899 }
900 if (isascii(c) && isprint(c))
901 {
902 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
903 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
904 }
905 else if (tTd(84, 2))
906 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c);
907 else if (tTd(84, 1))
908 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c);
909 else if (!isascii(c) && !tTd(84, 1))
910 {
911 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
912 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
913 }
914 }
915 if (shiftout)
916 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
917 TermEscape.te_normal);
918 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
919 }
920
921 /*
922 ** MAKELOWER -- Translate a line into lower case
923 **
924 ** Parameters:
925 ** p -- the string to translate. If NULL, return is
926 ** immediate.
927 **
928 ** Returns:
929 ** none.
930 **
931 ** Side Effects:
932 ** String pointed to by p is translated to lower case.
933 */
934
935 void
936 makelower(p)
937 register char *p;
938 {
939 register char c;
940
941 if (p == NULL)
942 return;
943 for (; (c = *p) != '\0'; p++)
944 if (isascii(c) && isupper(c))
945 *p = tolower(c);
946 }
947
948 /*
949 ** FIXCRLF -- fix <CR><LF> in line.
950 **
951 ** Looks for the <CR><LF> combination and turns it into the
952 ** UNIX canonical <NL> character. It only takes one line,
953 ** i.e., it is assumed that the first <NL> found is the end
954 ** of the line.
955 **
956 ** Parameters:
957 ** line -- the line to fix.
958 ** stripnl -- if true, strip the newline also.
959 **
960 ** Returns:
961 ** none.
962 **
963 ** Side Effects:
964 ** line is changed in place.
965 */
966
967 void
968 fixcrlf(line, stripnl)
969 char *line;
970 bool stripnl;
971 {
972 register char *p;
973
974 p = strchr(line, '\n');
975 if (p == NULL)
976 return;
977 if (p > line && p[-1] == '\r')
978 p--;
979 if (!stripnl)
980 *p++ = '\n';
981 *p = '\0';
982 }
983
984 /*
985 ** PUTLINE -- put a line like fputs obeying SMTP conventions
986 **
987 ** This routine always guarantees outputing a newline (or CRLF,
988 ** as appropriate) at the end of the string.
989 **
990 ** Parameters:
991 ** l -- line to put.
992 ** mci -- the mailer connection information.
993 **
994 ** Returns:
995 ** true iff line was written successfully
996 **
997 ** Side Effects:
998 ** output of l to mci->mci_out.
999 */
1000
1001 bool
1002 putline(l, mci)
1003 register char *l;
1004 register MCI *mci;
1005 {
1006 return putxline(l, strlen(l), mci, PXLF_MAPFROM);
1007 }
1008
1009 /*
1010 ** PUTXLINE -- putline with flags bits.
1011 **
1012 ** This routine always guarantees outputing a newline (or CRLF,
1013 ** as appropriate) at the end of the string.
1014 **
1015 ** Parameters:
1016 ** l -- line to put.
1017 ** len -- the length of the line.
1018 ** mci -- the mailer connection information.
1019 ** pxflags -- flag bits:
1020 ** PXLF_MAPFROM -- map From_ to >From_.
1021 ** PXLF_STRIP8BIT -- strip 8th bit.
1022 ** PXLF_HEADER -- map bare newline in header to newline space.
1023 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1024 ** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes.
1025 **
1026 ** Returns:
1027 ** true iff line was written successfully
1028 **
1029 ** Side Effects:
1030 ** output of l to mci->mci_out.
1031 */
1032
1033
1034 #define PUTX(limit) \
1035 do \
1036 { \
1037 quotenext = false; \
1038 while (l < limit) \
1039 { \
1040 unsigned char c = (unsigned char) *l++; \
1041 \
1042 if (bitset(PXLF_STRIPMQUOTE, pxflags) && \
1043 !quotenext && c == METAQUOTE) \
1044 { \
1045 quotenext = true; \
1046 continue; \
1047 } \
1048 quotenext = false; \
1049 if (strip8bit) \
1050 c &= 0177; \
1051 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \
1052 c) == SM_IO_EOF) \
1053 { \
1054 dead = true; \
1055 break; \
1056 } \
1057 if (TrafficLogFile != NULL) \
1058 (void) sm_io_putc(TrafficLogFile, \
1059 SM_TIME_DEFAULT, \
1060 c); \
1061 } \
1062 } while (0)
1063
1064 bool
1065 putxline(l, len, mci, pxflags)
1066 register char *l;
1067 size_t len;
1068 register MCI *mci;
1069 int pxflags;
1070 {
1071 register char *p, *end;
1072 int slop;
1073 bool dead, quotenext, strip8bit;
1074
1075 /* strip out 0200 bits -- these can look like TELNET protocol */
1076 strip8bit = bitset(MCIF_7BIT, mci->mci_flags) ||
1077 bitset(PXLF_STRIP8BIT, pxflags);
1078 dead = false;
1079 slop = 0;
1080
1081 end = l + len;
1082 do
1083 {
1084 bool noeol = false;
1085
1086 /* find the end of the line */
1087 p = memchr(l, '\n', end - l);
1088 if (p == NULL)
1089 {
1090 p = end;
1091 noeol = true;
1092 }
1093
1094 if (TrafficLogFile != NULL)
1095 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1096 "%05d >>> ", (int) CurrentPid);
1097
1098 /* check for line overflow */
1099 while (mci->mci_mailer->m_linelimit > 0 &&
1100 (p - l + slop) > mci->mci_mailer->m_linelimit)
1101 {
1102 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1103
1104 if (l[0] == '.' && slop == 0 &&
1105 bitnset(M_XDOT, mci->mci_mailer->m_flags))
1106 {
1107 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1108 '.') == SM_IO_EOF)
1109 dead = true;
1110 if (TrafficLogFile != NULL)
1111 (void) sm_io_putc(TrafficLogFile,
1112 SM_TIME_DEFAULT, '.');
1113 }
1114 else if (l[0] == 'F' && slop == 0 &&
1115 bitset(PXLF_MAPFROM, pxflags) &&
1116 strncmp(l, "From ", 5) == 0 &&
1117 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1118 {
1119 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1120 '>') == SM_IO_EOF)
1121 dead = true;
1122 if (TrafficLogFile != NULL)
1123 (void) sm_io_putc(TrafficLogFile,
1124 SM_TIME_DEFAULT,
1125 '>');
1126 }
1127 if (dead)
1128 break;
1129
1130 PUTX(q);
1131 if (dead)
1132 break;
1133
1134 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1135 '!') == SM_IO_EOF ||
1136 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1137 mci->mci_mailer->m_eol) == SM_IO_EOF ||
1138 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1139 ' ') == SM_IO_EOF)
1140 {
1141 dead = true;
1142 break;
1143 }
1144 if (TrafficLogFile != NULL)
1145 {
1146 (void) sm_io_fprintf(TrafficLogFile,
1147 SM_TIME_DEFAULT,
1148 "!\n%05d >>> ",
1149 (int) CurrentPid);
1150 }
1151 slop = 1;
1152 }
1153
1154 if (dead)
1155 break;
1156
1157 /* output last part */
1158 if (l[0] == '.' && slop == 0 &&
1159 bitnset(M_XDOT, mci->mci_mailer->m_flags) &&
1160 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1161 {
1162 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1163 SM_IO_EOF)
1164 {
1165 dead = true;
1166 break;
1167 }
1168 if (TrafficLogFile != NULL)
1169 (void) sm_io_putc(TrafficLogFile,
1170 SM_TIME_DEFAULT, '.');
1171 }
1172 else if (l[0] == 'F' && slop == 0 &&
1173 bitset(PXLF_MAPFROM, pxflags) &&
1174 strncmp(l, "From ", 5) == 0 &&
1175 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
1176 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1177 {
1178 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1179 SM_IO_EOF)
1180 {
1181 dead = true;
1182 break;
1183 }
1184 if (TrafficLogFile != NULL)
1185 (void) sm_io_putc(TrafficLogFile,
1186 SM_TIME_DEFAULT, '>');
1187 }
1188 PUTX(p);
1189 if (dead)
1190 break;
1191
1192 if (TrafficLogFile != NULL)
1193 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1194 '\n');
1195 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol))
1196 {
1197 mci->mci_flags &= ~MCIF_INLONGLINE;
1198 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1199 mci->mci_mailer->m_eol) == SM_IO_EOF)
1200 {
1201 dead = true;
1202 break;
1203 }
1204 }
1205 else
1206 mci->mci_flags |= MCIF_INLONGLINE;
1207
1208 if (l < end && *l == '\n')
1209 {
1210 if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1211 bitset(PXLF_HEADER, pxflags))
1212 {
1213 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1214 ' ') == SM_IO_EOF)
1215 {
1216 dead = true;
1217 break;
1218 }
1219
1220 if (TrafficLogFile != NULL)
1221 (void) sm_io_putc(TrafficLogFile,
1222 SM_TIME_DEFAULT, ' ');
1223 }
1224 }
1225
1226 } while (l < end);
1227 return !dead;
1228 }
1229
1230 /*
1231 ** XUNLINK -- unlink a file, doing logging as appropriate.
1232 **
1233 ** Parameters:
1234 ** f -- name of file to unlink.
1235 **
1236 ** Returns:
1237 ** return value of unlink()
1238 **
1239 ** Side Effects:
1240 ** f is unlinked.
1241 */
1242
1243 int
1244 xunlink(f)
1245 char *f;
1246 {
1247 register int i;
1248 int save_errno;
1249
1250 if (LogLevel > 98)
1251 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1252
1253 i = unlink(f);
1254 save_errno = errno;
1255 if (i < 0 && LogLevel > 97)
1256 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1257 f, errno);
1258 if (i >= 0)
1259 SYNC_DIR(f, false);
1260 errno = save_errno;
1261 return i;
1262 }
1263
1264 /*
1265 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1266 **
1267 ** Parameters:
1268 ** buf -- place to put the input line.
1269 ** siz -- size of buf.
1270 ** fp -- file to read from.
1271 ** timeout -- the timeout before error occurs.
1272 ** during -- what we are trying to read (for error messages).
1273 **
1274 ** Returns:
1275 ** NULL on error (including timeout). This may also leave
1276 ** buf containing a null string.
1277 ** buf otherwise.
1278 */
1279
1280
1281 char *
1282 sfgets(buf, siz, fp, timeout, during)
1283 char *buf;
1284 int siz;
1285 SM_FILE_T *fp;
1286 time_t timeout;
1287 char *during;
1288 {
1289 register char *p;
1290 int save_errno;
1291 int io_timeout;
1292
1293 SM_REQUIRE(siz > 0);
1294 SM_REQUIRE(buf != NULL);
1295
1296 if (fp == NULL)
1297 {
1298 buf[0] = '\0';
1299 errno = EBADF;
1300 return NULL;
1301 }
1302
1303 /* try to read */
1304 p = NULL;
1305 errno = 0;
1306
1307 /* convert the timeout to sm_io notation */
1308 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1309 while (!sm_io_eof(fp) && !sm_io_error(fp))
1310 {
1311 errno = 0;
1312 p = sm_io_fgets(fp, io_timeout, buf, siz);
1313 if (p == NULL && errno == EAGAIN)
1314 {
1315 /* The sm_io_fgets() call timedout */
1316 if (LogLevel > 1)
1317 sm_syslog(LOG_NOTICE, CurEnv->e_id,
1318 "timeout waiting for input from %.100s during %s",
1319 CURHOSTNAME,
1320 during);
1321 buf[0] = '\0';
1322 #if XDEBUG
1323 checkfd012(during);
1324 #endif /* XDEBUG */
1325 if (TrafficLogFile != NULL)
1326 (void) sm_io_fprintf(TrafficLogFile,
1327 SM_TIME_DEFAULT,
1328 "%05d <<< [TIMEOUT]\n",
1329 (int) CurrentPid);
1330 errno = ETIMEDOUT;
1331 return NULL;
1332 }
1333 if (p != NULL || errno != EINTR)
1334 break;
1335 (void) sm_io_clearerr(fp);
1336 }
1337 save_errno = errno;
1338
1339 /* clean up the books and exit */
1340 LineNumber++;
1341 if (p == NULL)
1342 {
1343 buf[0] = '\0';
1344 if (TrafficLogFile != NULL)
1345 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1346 "%05d <<< [EOF]\n",
1347 (int) CurrentPid);
1348 errno = save_errno;
1349 return NULL;
1350 }
1351 if (TrafficLogFile != NULL)
1352 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1353 "%05d <<< %s", (int) CurrentPid, buf);
1354 if (SevenBitInput)
1355 {
1356 for (p = buf; *p != '\0'; p++)
1357 *p &= ~0200;
1358 }
1359 else if (!HasEightBits)
1360 {
1361 for (p = buf; *p != '\0'; p++)
1362 {
1363 if (bitset(0200, *p))
1364 {
1365 HasEightBits = true;
1366 break;
1367 }
1368 }
1369 }
1370 return buf;
1371 }
1372
1373 /*
1374 ** FGETFOLDED -- like fgets, but knows about folded lines.
1375 **
1376 ** Parameters:
1377 ** buf -- place to put result.
1378 ** np -- pointer to bytes available; will be updated with
1379 ** the actual buffer size (not number of bytes filled)
1380 ** on return.
1381 ** f -- file to read from.
1382 **
1383 ** Returns:
1384 ** input line(s) on success, NULL on error or SM_IO_EOF.
1385 ** This will normally be buf -- unless the line is too
1386 ** long, when it will be sm_malloc_x()ed.
1387 **
1388 ** Side Effects:
1389 ** buf gets lines from f, with continuation lines (lines
1390 ** with leading white space) appended. CRLF's are mapped
1391 ** into single newlines. Any trailing NL is stripped.
1392 */
1393
1394 char *
1395 fgetfolded(buf, np, f)
1396 char *buf;
1397 int *np;
1398 SM_FILE_T *f;
1399 {
1400 register char *p = buf;
1401 char *bp = buf;
1402 register int i;
1403 int n;
1404
1405 SM_REQUIRE(np != NULL);
1406 n = *np;
1407 SM_REQUIRE(n > 0);
1408 SM_REQUIRE(buf != NULL);
1409 if (f == NULL)
1410 {
1411 buf[0] = '\0';
1412 errno = EBADF;
1413 return NULL;
1414 }
1415
1416 n--;
1417 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1418 {
1419 if (i == '\r')
1420 {
1421 i = sm_io_getc(f, SM_TIME_DEFAULT);
1422 if (i != '\n')
1423 {
1424 if (i != SM_IO_EOF)
1425 (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1426 i);
1427 i = '\r';
1428 }
1429 }
1430 if (--n <= 0)
1431 {
1432 /* allocate new space */
1433 char *nbp;
1434 int nn;
1435
1436 nn = (p - bp);
1437 if (nn < MEMCHUNKSIZE)
1438 nn *= 2;
1439 else
1440 nn += MEMCHUNKSIZE;
1441 nbp = sm_malloc_x(nn);
1442 memmove(nbp, bp, p - bp);
1443 p = &nbp[p - bp];
1444 if (bp != buf)
1445 sm_free(bp);
1446 bp = nbp;
1447 n = nn - (p - bp);
1448 *np = nn;
1449 }
1450 *p++ = i;
1451 if (i == '\n')
1452 {
1453 LineNumber++;
1454 i = sm_io_getc(f, SM_TIME_DEFAULT);
1455 if (i != SM_IO_EOF)
1456 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1457 if (i != ' ' && i != '\t')
1458 break;
1459 }
1460 }
1461 if (p == bp)
1462 return NULL;
1463 if (p[-1] == '\n')
1464 p--;
1465 *p = '\0';
1466 return bp;
1467 }
1468
1469 /*
1470 ** CURTIME -- return current time.
1471 **
1472 ** Parameters:
1473 ** none.
1474 **
1475 ** Returns:
1476 ** the current time.
1477 */
1478
1479 time_t
1480 curtime()
1481 {
1482 auto time_t t;
1483
1484 (void) time(&t);
1485 return t;
1486 }
1487
1488 /*
1489 ** ATOBOOL -- convert a string representation to boolean.
1490 **
1491 ** Defaults to false
1492 **
1493 ** Parameters:
1494 ** s -- string to convert. Takes "tTyY", empty, and NULL as true,
1495 ** others as false.
1496 **
1497 ** Returns:
1498 ** A boolean representation of the string.
1499 */
1500
1501 bool
1502 atobool(s)
1503 register char *s;
1504 {
1505 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1506 return true;
1507 return false;
1508 }
1509
1510 /*
1511 ** ATOOCT -- convert a string representation to octal.
1512 **
1513 ** Parameters:
1514 ** s -- string to convert.
1515 **
1516 ** Returns:
1517 ** An integer representing the string interpreted as an
1518 ** octal number.
1519 */
1520
1521 int
1522 atooct(s)
1523 register char *s;
1524 {
1525 register int i = 0;
1526
1527 while (*s >= '0' && *s <= '7')
1528 i = (i << 3) | (*s++ - '0');
1529 return i;
1530 }
1531
1532 /*
1533 ** BITINTERSECT -- tell if two bitmaps intersect
1534 **
1535 ** Parameters:
1536 ** a, b -- the bitmaps in question
1537 **
1538 ** Returns:
1539 ** true if they have a non-null intersection
1540 ** false otherwise
1541 */
1542
1543 bool
1544 bitintersect(a, b)
1545 BITMAP256 a;
1546 BITMAP256 b;
1547 {
1548 int i;
1549
1550 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1551 {
1552 if ((a[i] & b[i]) != 0)
1553 return true;
1554 }
1555 return false;
1556 }
1557
1558 /*
1559 ** BITZEROP -- tell if a bitmap is all zero
1560 **
1561 ** Parameters:
1562 ** map -- the bit map to check
1563 **
1564 ** Returns:
1565 ** true if map is all zero.
1566 ** false if there are any bits set in map.
1567 */
1568
1569 bool
1570 bitzerop(map)
1571 BITMAP256 map;
1572 {
1573 int i;
1574
1575 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1576 {
1577 if (map[i] != 0)
1578 return false;
1579 }
1580 return true;
1581 }
1582
1583 /*
1584 ** STRCONTAINEDIN -- tell if one string is contained in another
1585 **
1586 ** Parameters:
1587 ** icase -- ignore case?
1588 ** a -- possible substring.
1589 ** b -- possible superstring.
1590 **
1591 ** Returns:
1592 ** true if a is contained in b (case insensitive).
1593 ** false otherwise.
1594 */
1595
1596 bool
1597 strcontainedin(icase, a, b)
1598 bool icase;
1599 register char *a;
1600 register char *b;
1601 {
1602 int la;
1603 int lb;
1604 int c;
1605
1606 la = strlen(a);
1607 lb = strlen(b);
1608 c = *a;
1609 if (icase && isascii(c) && isupper(c))
1610 c = tolower(c);
1611 for (; lb-- >= la; b++)
1612 {
1613 if (icase)
1614 {
1615 if (*b != c &&
1616 isascii(*b) && isupper(*b) && tolower(*b) != c)
1617 continue;
1618 if (sm_strncasecmp(a, b, la) == 0)
1619 return true;
1620 }
1621 else
1622 {
1623 if (*b != c)
1624 continue;
1625 if (strncmp(a, b, la) == 0)
1626 return true;
1627 }
1628 }
1629 return false;
1630 }
1631
1632 /*
1633 ** CHECKFD012 -- check low numbered file descriptors
1634 **
1635 ** File descriptors 0, 1, and 2 should be open at all times.
1636 ** This routine verifies that, and fixes it if not true.
1637 **
1638 ** Parameters:
1639 ** where -- a tag printed if the assertion failed
1640 **
1641 ** Returns:
1642 ** none
1643 */
1644
1645 void
1646 checkfd012(where)
1647 char *where;
1648 {
1649 #if XDEBUG
1650 register int i;
1651
1652 for (i = 0; i < 3; i++)
1653 fill_fd(i, where);
1654 #endif /* XDEBUG */
1655 }
1656
1657 /*
1658 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1659 **
1660 ** Parameters:
1661 ** fd -- file descriptor to check.
1662 ** where -- tag to print on failure.
1663 **
1664 ** Returns:
1665 ** none.
1666 */
1667
1668 void
1669 checkfdopen(fd, where)
1670 int fd;
1671 char *where;
1672 {
1673 #if XDEBUG
1674 struct stat st;
1675
1676 if (fstat(fd, &st) < 0 && errno == EBADF)
1677 {
1678 syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1679 printopenfds(true);
1680 }
1681 #endif /* XDEBUG */
1682 }
1683
1684 /*
1685 ** CHECKFDS -- check for new or missing file descriptors
1686 **
1687 ** Parameters:
1688 ** where -- tag for printing. If null, take a base line.
1689 **
1690 ** Returns:
1691 ** none
1692 **
1693 ** Side Effects:
1694 ** If where is set, shows changes since the last call.
1695 */
1696
1697 void
1698 checkfds(where)
1699 char *where;
1700 {
1701 int maxfd;
1702 register int fd;
1703 bool printhdr = true;
1704 int save_errno = errno;
1705 static BITMAP256 baseline;
1706 extern int DtableSize;
1707
1708 if (DtableSize > BITMAPBITS)
1709 maxfd = BITMAPBITS;
1710 else
1711 maxfd = DtableSize;
1712 if (where == NULL)
1713 clrbitmap(baseline);
1714
1715 for (fd = 0; fd < maxfd; fd++)
1716 {
1717 struct stat stbuf;
1718
1719 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1720 {
1721 if (!bitnset(fd, baseline))
1722 continue;
1723 clrbitn(fd, baseline);
1724 }
1725 else if (!bitnset(fd, baseline))
1726 setbitn(fd, baseline);
1727 else
1728 continue;
1729
1730 /* file state has changed */
1731 if (where == NULL)
1732 continue;
1733 if (printhdr)
1734 {
1735 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1736 "%s: changed fds:",
1737 where);
1738 printhdr = false;
1739 }
1740 dumpfd(fd, true, true);
1741 }
1742 errno = save_errno;
1743 }
1744
1745 /*
1746 ** PRINTOPENFDS -- print the open file descriptors (for debugging)
1747 **
1748 ** Parameters:
1749 ** logit -- if set, send output to syslog; otherwise
1750 ** print for debugging.
1751 **
1752 ** Returns:
1753 ** none.
1754 */
1755
1756 #if NETINET || NETINET6
1757 # include <arpa/inet.h>
1758 #endif /* NETINET || NETINET6 */
1759
1760 void
1761 printopenfds(logit)
1762 bool logit;
1763 {
1764 register int fd;
1765 extern int DtableSize;
1766
1767 for (fd = 0; fd < DtableSize; fd++)
1768 dumpfd(fd, false, logit);
1769 }
1770
1771 /*
1772 ** DUMPFD -- dump a file descriptor
1773 **
1774 ** Parameters:
1775 ** fd -- the file descriptor to dump.
1776 ** printclosed -- if set, print a notification even if
1777 ** it is closed; otherwise print nothing.
1778 ** logit -- if set, use sm_syslog instead of sm_dprintf()
1779 **
1780 ** Returns:
1781 ** none.
1782 */
1783
1784 void
1785 dumpfd(fd, printclosed, logit)
1786 int fd;
1787 bool printclosed;
1788 bool logit;
1789 {
1790 register char *p;
1791 char *hp;
1792 #ifdef S_IFSOCK
1793 SOCKADDR sa;
1794 #endif /* S_IFSOCK */
1795 auto SOCKADDR_LEN_T slen;
1796 int i;
1797 #if STAT64 > 0
1798 struct stat64 st;
1799 #else /* STAT64 > 0 */
1800 struct stat st;
1801 #endif /* STAT64 > 0 */
1802 char buf[200];
1803
1804 p = buf;
1805 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1806 p += strlen(p);
1807
1808 if (
1809 #if STAT64 > 0
1810 fstat64(fd, &st)
1811 #else /* STAT64 > 0 */
1812 fstat(fd, &st)
1813 #endif /* STAT64 > 0 */
1814 < 0)
1815 {
1816 if (errno != EBADF)
1817 {
1818 (void) sm_snprintf(p, SPACELEFT(buf, p),
1819 "CANNOT STAT (%s)",
1820 sm_errstring(errno));
1821 goto printit;
1822 }
1823 else if (printclosed)
1824 {
1825 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1826 goto printit;
1827 }
1828 return;
1829 }
1830
1831 i = fcntl(fd, F_GETFL, 0);
1832 if (i != -1)
1833 {
1834 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1835 p += strlen(p);
1836 }
1837
1838 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1839 (int) st.st_mode);
1840 p += strlen(p);
1841 switch (st.st_mode & S_IFMT)
1842 {
1843 #ifdef S_IFSOCK
1844 case S_IFSOCK:
1845 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1846 p += strlen(p);
1847 memset(&sa, '\0', sizeof(sa));
1848 slen = sizeof(sa);
1849 if (getsockname(fd, &sa.sa, &slen) < 0)
1850 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1851 sm_errstring(errno));
1852 else
1853 {
1854 hp = hostnamebyanyaddr(&sa);
1855 if (hp == NULL)
1856 {
1857 /* EMPTY */
1858 /* do nothing */
1859 }
1860 # if NETINET
1861 else if (sa.sa.sa_family == AF_INET)
1862 (void) sm_snprintf(p, SPACELEFT(buf, p),
1863 "%s/%d", hp, ntohs(sa.sin.sin_port));
1864 # endif /* NETINET */
1865 # if NETINET6
1866 else if (sa.sa.sa_family == AF_INET6)
1867 (void) sm_snprintf(p, SPACELEFT(buf, p),
1868 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1869 # endif /* NETINET6 */
1870 else
1871 (void) sm_snprintf(p, SPACELEFT(buf, p),
1872 "%s", hp);
1873 }
1874 p += strlen(p);
1875 (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
1876 p += strlen(p);
1877 slen = sizeof(sa);
1878 if (getpeername(fd, &sa.sa, &slen) < 0)
1879 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1880 sm_errstring(errno));
1881 else
1882 {
1883 hp = hostnamebyanyaddr(&sa);
1884 if (hp == NULL)
1885 {
1886 /* EMPTY */
1887 /* do nothing */
1888 }
1889 # if NETINET
1890 else if (sa.sa.sa_family == AF_INET)
1891 (void) sm_snprintf(p, SPACELEFT(buf, p),
1892 "%s/%d", hp, ntohs(sa.sin.sin_port));
1893 # endif /* NETINET */
1894 # if NETINET6
1895 else if (sa.sa.sa_family == AF_INET6)
1896 (void) sm_snprintf(p, SPACELEFT(buf, p),
1897 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1898 # endif /* NETINET6 */
1899 else
1900 (void) sm_snprintf(p, SPACELEFT(buf, p),
1901 "%s", hp);
1902 }
1903 break;
1904 #endif /* S_IFSOCK */
1905
1906 case S_IFCHR:
1907 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
1908 p += strlen(p);
1909 goto defprint;
1910
1911 #ifdef S_IFBLK
1912 case S_IFBLK:
1913 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
1914 p += strlen(p);
1915 goto defprint;
1916 #endif /* S_IFBLK */
1917
1918 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1919 case S_IFIFO:
1920 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1921 p += strlen(p);
1922 goto defprint;
1923 #endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1924
1925 #ifdef S_IFDIR
1926 case S_IFDIR:
1927 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
1928 p += strlen(p);
1929 goto defprint;
1930 #endif /* S_IFDIR */
1931
1932 #ifdef S_IFLNK
1933 case S_IFLNK:
1934 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
1935 p += strlen(p);
1936 goto defprint;
1937 #endif /* S_IFLNK */
1938
1939 default:
1940 defprint:
1941 (void) sm_snprintf(p, SPACELEFT(buf, p),
1942 "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
1943 major(st.st_dev), minor(st.st_dev),
1944 (ULONGLONG_T) st.st_ino,
1945 (int) st.st_nlink, (int) st.st_uid,
1946 (int) st.st_gid);
1947 p += strlen(p);
1948 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
1949 (ULONGLONG_T) st.st_size);
1950 break;
1951 }
1952
1953 printit:
1954 if (logit)
1955 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1956 "%.800s", buf);
1957 else
1958 sm_dprintf("%s\n", buf);
1959 }
1960
1961 /*
1962 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1963 **
1964 ** Parameters:
1965 ** host -- the host to shorten (stripped in place).
1966 **
1967 ** Returns:
1968 ** place where string was truncated, NULL if not truncated.
1969 */
1970
1971 char *
1972 shorten_hostname(host)
1973 char host[];
1974 {
1975 register char *p;
1976 char *mydom;
1977 int i;
1978 bool canon = false;
1979
1980 /* strip off final dot */
1981 i = strlen(host);
1982 p = &host[(i == 0) ? 0 : i - 1];
1983 if (*p == '.')
1984 {
1985 *p = '\0';
1986 canon = true;
1987 }
1988
1989 /* see if there is any domain at all -- if not, we are done */
1990 p = strchr(host, '.');
1991 if (p == NULL)
1992 return NULL;
1993
1994 /* yes, we have a domain -- see if it looks like us */
1995 mydom = macvalue('m', CurEnv);
1996 if (mydom == NULL)
1997 mydom = "";
1998 i = strlen(++p);
1999 if ((canon ? sm_strcasecmp(p, mydom)
2000 : sm_strncasecmp(p, mydom, i)) == 0 &&
2001 (mydom[i] == '.' || mydom[i] == '\0'))
2002 {
2003 *--p = '\0';
2004 return p;
2005 }
2006 return NULL;
2007 }
2008
2009 /*
2010 ** PROG_OPEN -- open a program for reading
2011 **
2012 ** Parameters:
2013 ** argv -- the argument list.
2014 ** pfd -- pointer to a place to store the file descriptor.
2015 ** e -- the current envelope.
2016 **
2017 ** Returns:
2018 ** pid of the process -- -1 if it failed.
2019 */
2020
2021 pid_t
2022 prog_open(argv, pfd, e)
2023 char **argv;
2024 int *pfd;
2025 ENVELOPE *e;
2026 {
2027 pid_t pid;
2028 int save_errno;
2029 int sff;
2030 int ret;
2031 int fdv[2];
2032 char *p, *q;
2033 char buf[MAXPATHLEN];
2034 extern int DtableSize;
2035
2036 if (pipe(fdv) < 0)
2037 {
2038 syserr("%s: cannot create pipe for stdout", argv[0]);
2039 return -1;
2040 }
2041 pid = fork();
2042 if (pid < 0)
2043 {
2044 syserr("%s: cannot fork", argv[0]);
2045 (void) close(fdv[0]);
2046 (void) close(fdv[1]);
2047 return -1;
2048 }
2049 if (pid > 0)
2050 {
2051 /* parent */
2052 (void) close(fdv[1]);
2053 *pfd = fdv[0];
2054 return pid;
2055 }
2056
2057 /* Reset global flags */
2058 RestartRequest = NULL;
2059 RestartWorkGroup = false;
2060 ShutdownRequest = NULL;
2061 PendingSignal = 0;
2062 CurrentPid = getpid();
2063
2064 /*
2065 ** Initialize exception stack and default exception
2066 ** handler for child process.
2067 */
2068
2069 sm_exc_newthread(fatal_error);
2070
2071 /* child -- close stdin */
2072 (void) close(0);
2073
2074 /* stdout goes back to parent */
2075 (void) close(fdv[0]);
2076 if (dup2(fdv[1], 1) < 0)
2077 {
2078 syserr("%s: cannot dup2 for stdout", argv[0]);
2079 _exit(EX_OSERR);
2080 }
2081 (void) close(fdv[1]);
2082
2083 /* stderr goes to transcript if available */
2084 if (e->e_xfp != NULL)
2085 {
2086 int xfd;
2087
2088 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
2089 if (xfd >= 0 && dup2(xfd, 2) < 0)
2090 {
2091 syserr("%s: cannot dup2 for stderr", argv[0]);
2092 _exit(EX_OSERR);
2093 }
2094 }
2095
2096 /* this process has no right to the queue file */
2097 if (e->e_lockfp != NULL)
2098 {
2099 int fd;
2100
2101 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
2102 if (fd >= 0)
2103 (void) close(fd);
2104 else
2105 syserr("%s: lockfp does not have a fd", argv[0]);
2106 }
2107
2108 /* chroot to the program mailer directory, if defined */
2109 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2110 {
2111 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e);
2112 if (chroot(buf) < 0)
2113 {
2114 syserr("prog_open: cannot chroot(%s)", buf);
2115 exit(EX_TEMPFAIL);
2116 }
2117 if (chdir("/") < 0)
2118 {
2119 syserr("prog_open: cannot chdir(/)");
2120 exit(EX_TEMPFAIL);
2121 }
2122 }
2123
2124 /* run as default user */
2125 endpwent();
2126 sm_mbdb_terminate();
2127 #if _FFR_MEMSTAT
2128 (void) sm_memstat_close();
2129 #endif /* _FFR_MEMSTAT */
2130 if (setgid(DefGid) < 0 && geteuid() == 0)
2131 {
2132 syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2133 exit(EX_TEMPFAIL);
2134 }
2135 if (setuid(DefUid) < 0 && geteuid() == 0)
2136 {
2137 syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2138 exit(EX_TEMPFAIL);
2139 }
2140
2141 /* run in some directory */
2142 if (ProgMailer != NULL)
2143 p = ProgMailer->m_execdir;
2144 else
2145 p = NULL;
2146 for (; p != NULL; p = q)
2147 {
2148 q = strchr(p, ':');
2149 if (q != NULL)
2150 *q = '\0';
2151 expand(p, buf, sizeof(buf), e);
2152 if (q != NULL)
2153 *q++ = ':';
2154 if (buf[0] != '\0' && chdir(buf) >= 0)
2155 break;
2156 }
2157 if (p == NULL)
2158 {
2159 /* backup directories */
2160 if (chdir("/tmp") < 0)
2161 (void) chdir("/");
2162 }
2163
2164 /* Check safety of program to be run */
2165 sff = SFF_ROOTOK|SFF_EXECOK;
2166 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2167 sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2168 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2169 sff |= SFF_NOPATHCHECK;
2170 else
2171 sff |= SFF_SAFEDIRPATH;
2172 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2173 if (ret != 0)
2174 sm_syslog(LOG_INFO, e->e_id,
2175 "Warning: prog_open: program %s unsafe: %s",
2176 argv[0], sm_errstring(ret));
2177
2178 /* arrange for all the files to be closed */
2179 sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2180
2181 /* now exec the process */
2182 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2183
2184 /* woops! failed */
2185 save_errno = errno;
2186 syserr("%s: cannot exec", argv[0]);
2187 if (transienterror(save_errno))
2188 _exit(EX_OSERR);
2189 _exit(EX_CONFIG);
2190 return -1; /* avoid compiler warning on IRIX */
2191 }
2192
2193 /*
2194 ** GET_COLUMN -- look up a Column in a line buffer
2195 **
2196 ** Parameters:
2197 ** line -- the raw text line to search.
2198 ** col -- the column number to fetch.
2199 ** delim -- the delimiter between columns. If null,
2200 ** use white space.
2201 ** buf -- the output buffer.
2202 ** buflen -- the length of buf.
2203 **
2204 ** Returns:
2205 ** buf if successful.
2206 ** NULL otherwise.
2207 */
2208
2209 char *
2210 get_column(line, col, delim, buf, buflen)
2211 char line[];
2212 int col;
2213 int delim;
2214 char buf[];
2215 int buflen;
2216 {
2217 char *p;
2218 char *begin, *end;
2219 int i;
2220 char delimbuf[4];
2221
2222 if ((char) delim == '\0')
2223 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf));
2224 else
2225 {
2226 delimbuf[0] = (char) delim;
2227 delimbuf[1] = '\0';
2228 }
2229
2230 p = line;
2231 if (*p == '\0')
2232 return NULL; /* line empty */
2233 if (*p == (char) delim && col == 0)
2234 return NULL; /* first column empty */
2235
2236 begin = line;
2237
2238 if (col == 0 && (char) delim == '\0')
2239 {
2240 while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2241 begin++;
2242 }
2243
2244 for (i = 0; i < col; i++)
2245 {
2246 if ((begin = strpbrk(begin, delimbuf)) == NULL)
2247 return NULL; /* no such column */
2248 begin++;
2249 if ((char) delim == '\0')
2250 {
2251 while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2252 begin++;
2253 }
2254 }
2255
2256 end = strpbrk(begin, delimbuf);
2257 if (end == NULL)
2258 i = strlen(begin);
2259 else
2260 i = end - begin;
2261 if (i >= buflen)
2262 i = buflen - 1;
2263 (void) sm_strlcpy(buf, begin, i + 1);
2264 return buf;
2265 }
2266
2267 /*
2268 ** CLEANSTRCPY -- copy string keeping out bogus characters
2269 **
2270 ** Parameters:
2271 ** t -- "to" string.
2272 ** f -- "from" string.
2273 ** l -- length of space available in "to" string.
2274 **
2275 ** Returns:
2276 ** none.
2277 */
2278
2279 void
2280 cleanstrcpy(t, f, l)
2281 register char *t;
2282 register char *f;
2283 int l;
2284 {
2285 /* check for newlines and log if necessary */
2286 (void) denlstring(f, true, true);
2287
2288 if (l <= 0)
2289 syserr("!cleanstrcpy: length == 0");
2290
2291 l--;
2292 while (l > 0 && *f != '\0')
2293 {
2294 if (isascii(*f) &&
2295 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2296 {
2297 l--;
2298 *t++ = *f;
2299 }
2300 f++;
2301 }
2302 *t = '\0';
2303 }
2304
2305 /*
2306 ** DENLSTRING -- convert newlines in a string to spaces
2307 **
2308 ** Parameters:
2309 ** s -- the input string
2310 ** strict -- if set, don't permit continuation lines.
2311 ** logattacks -- if set, log attempted attacks.
2312 **
2313 ** Returns:
2314 ** A pointer to a version of the string with newlines
2315 ** mapped to spaces. This should be copied.
2316 */
2317
2318 char *
2319 denlstring(s, strict, logattacks)
2320 char *s;
2321 bool strict;
2322 bool logattacks;
2323 {
2324 register char *p;
2325 int l;
2326 static char *bp = NULL;
2327 static int bl = 0;
2328
2329 p = s;
2330 while ((p = strchr(p, '\n')) != NULL)
2331 if (strict || (*++p != ' ' && *p != '\t'))
2332 break;
2333 if (p == NULL)
2334 return s;
2335
2336 l = strlen(s) + 1;
2337 if (bl < l)
2338 {
2339 /* allocate more space */
2340 char *nbp = sm_pmalloc_x(l);
2341
2342 if (bp != NULL)
2343 sm_free(bp);
2344 bp = nbp;
2345 bl = l;
2346 }
2347 (void) sm_strlcpy(bp, s, l);
2348 for (p = bp; (p = strchr(p, '\n')) != NULL; )
2349 *p++ = ' ';
2350
2351 if (logattacks)
2352 {
2353 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL,
2354 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2355 RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2356 shortenstring(bp, MAXSHORTSTR));
2357 }
2358
2359 return bp;
2360 }
2361
2362 /*
2363 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2364 **
2365 ** Parameters:
2366 ** s -- string to manipulate (in place)
2367 ** subst -- character to use as replacement
2368 **
2369 ** Returns:
2370 ** true iff string did not contain "unprintable" characters
2371 */
2372
2373 bool
2374 strreplnonprt(s, c)
2375 char *s;
2376 int c;
2377 {
2378 bool ok;
2379
2380 ok = true;
2381 if (s == NULL)
2382 return ok;
2383 while (*s != '\0')
2384 {
2385 if (!(isascii(*s) && isprint(*s)))
2386 {
2387 *s = c;
2388 ok = false;
2389 }
2390 ++s;
2391 }
2392 return ok;
2393 }
2394
2395 /*
2396 ** PATH_IS_DIR -- check to see if file exists and is a directory.
2397 **
2398 ** There are some additional checks for security violations in
2399 ** here. This routine is intended to be used for the host status
2400 ** support.
2401 **
2402 ** Parameters:
2403 ** pathname -- pathname to check for directory-ness.
2404 ** createflag -- if set, create directory if needed.
2405 **
2406 ** Returns:
2407 ** true -- if the indicated pathname is a directory
2408 ** false -- otherwise
2409 */
2410
2411 bool
2412 path_is_dir(pathname, createflag)
2413 char *pathname;
2414 bool createflag;
2415 {
2416 struct stat statbuf;
2417
2418 #if HASLSTAT
2419 if (lstat(pathname, &statbuf) < 0)
2420 #else /* HASLSTAT */
2421 if (stat(pathname, &statbuf) < 0)
2422 #endif /* HASLSTAT */
2423 {
2424 if (errno != ENOENT || !createflag)
2425 return false;
2426 if (mkdir(pathname, 0755) < 0)
2427 return false;
2428 return true;
2429 }
2430 if (!S_ISDIR(statbuf.st_mode))
2431 {
2432 errno = ENOTDIR;
2433 return false;
2434 }
2435
2436 /* security: don't allow writable directories */
2437 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2438 {
2439 errno = EACCES;
2440 return false;
2441 }
2442 return true;
2443 }
2444
2445 /*
2446 ** PROC_LIST_ADD -- add process id to list of our children
2447 **
2448 ** Parameters:
2449 ** pid -- pid to add to list.
2450 ** task -- task of pid.
2451 ** type -- type of process.
2452 ** count -- number of processes.
2453 ** other -- other information for this type.
2454 **
2455 ** Returns:
2456 ** none
2457 **
2458 ** Side Effects:
2459 ** May increase CurChildren. May grow ProcList.
2460 */
2461
2462 typedef struct procs PROCS_T;
2463
2464 struct procs
2465 {
2466 pid_t proc_pid;
2467 char *proc_task;
2468 int proc_type;
2469 int proc_count;
2470 int proc_other;
2471 SOCKADDR proc_hostaddr;
2472 };
2473
2474 static PROCS_T *volatile ProcListVec = NULL;
2475 static int ProcListSize = 0;
2476
2477 void
2478 proc_list_add(pid, task, type, count, other, hostaddr)
2479 pid_t pid;
2480 char *task;
2481 int type;
2482 int count;
2483 int other;
2484 SOCKADDR *hostaddr;
2485 {
2486 int i;
2487
2488 for (i = 0; i < ProcListSize; i++)
2489 {
2490 if (ProcListVec[i].proc_pid == NO_PID)
2491 break;
2492 }
2493 if (i >= ProcListSize)
2494 {
2495 /* probe the existing vector to avoid growing infinitely */
2496 proc_list_probe();
2497
2498 /* now scan again */
2499 for (i = 0; i < ProcListSize; i++)
2500 {
2501 if (ProcListVec[i].proc_pid == NO_PID)
2502 break;
2503 }
2504 }
2505 if (i >= ProcListSize)
2506 {
2507 /* grow process list */
2508 int chldwasblocked;
2509 PROCS_T *npv;
2510
2511 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2512 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) *
2513 (ProcListSize + PROC_LIST_SEG));
2514
2515 /* Block SIGCHLD so reapchild() doesn't mess with us */
2516 chldwasblocked = sm_blocksignal(SIGCHLD);
2517 if (ProcListSize > 0)
2518 {
2519 memmove(npv, ProcListVec,
2520 ProcListSize * sizeof(PROCS_T));
2521 sm_free(ProcListVec);
2522 }
2523
2524 /* XXX just use memset() to initialize this part? */
2525 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2526 {
2527 npv[i].proc_pid = NO_PID;
2528 npv[i].proc_task = NULL;
2529 npv[i].proc_type = PROC_NONE;
2530 }
2531 i = ProcListSize;
2532 ProcListSize += PROC_LIST_SEG;
2533 ProcListVec = npv;
2534 if (chldwasblocked == 0)
2535 (void) sm_releasesignal(SIGCHLD);
2536 }
2537 ProcListVec[i].proc_pid = pid;
2538 PSTRSET(ProcListVec[i].proc_task, task);
2539 ProcListVec[i].proc_type = type;
2540 ProcListVec[i].proc_count = count;
2541 ProcListVec[i].proc_other = other;
2542 if (hostaddr != NULL)
2543 ProcListVec[i].proc_hostaddr = *hostaddr;
2544 else
2545 memset(&ProcListVec[i].proc_hostaddr, 0,
2546 sizeof(ProcListVec[i].proc_hostaddr));
2547
2548 /* if process adding itself, it's not a child */
2549 if (pid != CurrentPid)
2550 {
2551 SM_ASSERT(CurChildren < INT_MAX);
2552 CurChildren++;
2553 }
2554 }
2555
2556 /*
2557 ** PROC_LIST_SET -- set pid task in process list
2558 **
2559 ** Parameters:
2560 ** pid -- pid to set
2561 ** task -- task of pid
2562 **
2563 ** Returns:
2564 ** none.
2565 */
2566
2567 void
2568 proc_list_set(pid, task)
2569 pid_t pid;
2570 char *task;
2571 {
2572 int i;
2573
2574 for (i = 0; i < ProcListSize; i++)
2575 {
2576 if (ProcListVec[i].proc_pid == pid)
2577 {
2578 PSTRSET(ProcListVec[i].proc_task, task);
2579 break;
2580 }
2581 }
2582 }
2583
2584 /*
2585 ** PROC_LIST_DROP -- drop pid from process list
2586 **
2587 ** Parameters:
2588 ** pid -- pid to drop
2589 ** st -- process status
2590 ** other -- storage for proc_other (return).
2591 **
2592 ** Returns:
2593 ** none.
2594 **
2595 ** Side Effects:
2596 ** May decrease CurChildren, CurRunners, or
2597 ** set RestartRequest or ShutdownRequest.
2598 **
2599 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2600 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2601 ** DOING.
2602 */
2603
2604 void
2605 proc_list_drop(pid, st, other)
2606 pid_t pid;
2607 int st;
2608 int *other;
2609 {
2610 int i;
2611 int type = PROC_NONE;
2612
2613 for (i = 0; i < ProcListSize; i++)
2614 {
2615 if (ProcListVec[i].proc_pid == pid)
2616 {
2617 ProcListVec[i].proc_pid = NO_PID;
2618 type = ProcListVec[i].proc_type;
2619 if (other != NULL)
2620 *other = ProcListVec[i].proc_other;
2621 if (CurChildren > 0)
2622 CurChildren--;
2623 break;
2624 }
2625 }
2626
2627
2628 if (type == PROC_CONTROL && WIFEXITED(st))
2629 {
2630 /* if so, see if we need to restart or shutdown */
2631 if (WEXITSTATUS(st) == EX_RESTART)
2632 RestartRequest = "control socket";
2633 else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2634 ShutdownRequest = "control socket";
2635 }
2636 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2637 ProcListVec[i].proc_other > -1)
2638 {
2639 /* restart this persistent runner */
2640 mark_work_group_restart(ProcListVec[i].proc_other, st);
2641 }
2642 else if (type == PROC_QUEUE)
2643 CurRunners -= ProcListVec[i].proc_count;
2644 }
2645
2646 /*
2647 ** PROC_LIST_CLEAR -- clear the process list
2648 **
2649 ** Parameters:
2650 ** none.
2651 **
2652 ** Returns:
2653 ** none.
2654 **
2655 ** Side Effects:
2656 ** Sets CurChildren to zero.
2657 */
2658
2659 void
2660 proc_list_clear()
2661 {
2662 int i;
2663
2664 /* start from 1 since 0 is the daemon itself */
2665 for (i = 1; i < ProcListSize; i++)
2666 ProcListVec[i].proc_pid = NO_PID;
2667 CurChildren = 0;
2668 }
2669
2670 /*
2671 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2672 **
2673 ** Parameters:
2674 ** none
2675 **
2676 ** Returns:
2677 ** none
2678 **
2679 ** Side Effects:
2680 ** May decrease CurChildren.
2681 */
2682
2683 void
2684 proc_list_probe()
2685 {
2686 int i, children;
2687 int chldwasblocked;
2688 pid_t pid;
2689
2690 children = 0;
2691 chldwasblocked = sm_blocksignal(SIGCHLD);
2692
2693 /* start from 1 since 0 is the daemon itself */
2694 for (i = 1; i < ProcListSize; i++)
2695 {
2696 pid = ProcListVec[i].proc_pid;
2697 if (pid == NO_PID || pid == CurrentPid)
2698 continue;
2699 if (kill(pid, 0) < 0)
2700 {
2701 if (LogLevel > 3)
2702 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2703 "proc_list_probe: lost pid %d",
2704 (int) ProcListVec[i].proc_pid);
2705 ProcListVec[i].proc_pid = NO_PID;
2706 SM_FREE_CLR(ProcListVec[i].proc_task);
2707 CurChildren--;
2708 }
2709 else
2710 {
2711 ++children;
2712 }
2713 }
2714 if (CurChildren < 0)
2715 CurChildren = 0;
2716 if (chldwasblocked == 0)
2717 (void) sm_releasesignal(SIGCHLD);
2718 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
2719 {
2720 sm_syslog(LOG_ERR, NOQID,
2721 "proc_list_probe: found %d children, expected %d",
2722 children, CurChildren);
2723 }
2724 }
2725
2726 /*
2727 ** PROC_LIST_DISPLAY -- display the process list
2728 **
2729 ** Parameters:
2730 ** out -- output file pointer
2731 ** prefix -- string to output in front of each line.
2732 **
2733 ** Returns:
2734 ** none.
2735 */
2736
2737 void
2738 proc_list_display(out, prefix)
2739 SM_FILE_T *out;
2740 char *prefix;
2741 {
2742 int i;
2743
2744 for (i = 0; i < ProcListSize; i++)
2745 {
2746 if (ProcListVec[i].proc_pid == NO_PID)
2747 continue;
2748
2749 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2750 prefix,
2751 (int) ProcListVec[i].proc_pid,
2752 ProcListVec[i].proc_task != NULL ?
2753 ProcListVec[i].proc_task : "(unknown)",
2754 (OpMode == MD_SMTP ||
2755 OpMode == MD_DAEMON ||
2756 OpMode == MD_ARPAFTP) ? "\r" : "");
2757 }
2758 }
2759
2760 /*
2761 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2762 **
2763 ** Parameters:
2764 ** type -- type of process to signal
2765 ** signal -- the type of signal to send
2766 **
2767 ** Results:
2768 ** none.
2769 **
2770 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2771 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2772 ** DOING.
2773 */
2774
2775 void
2776 proc_list_signal(type, signal)
2777 int type;
2778 int signal;
2779 {
2780 int chldwasblocked;
2781 int alrmwasblocked;
2782 int i;
2783 pid_t mypid = getpid();
2784
2785 /* block these signals so that we may signal cleanly */
2786 chldwasblocked = sm_blocksignal(SIGCHLD);
2787 alrmwasblocked = sm_blocksignal(SIGALRM);
2788
2789 /* Find all processes of type and send signal */
2790 for (i = 0; i < ProcListSize; i++)
2791 {
2792 if (ProcListVec[i].proc_pid == NO_PID ||
2793 ProcListVec[i].proc_pid == mypid)
2794 continue;
2795 if (ProcListVec[i].proc_type != type)
2796 continue;
2797 (void) kill(ProcListVec[i].proc_pid, signal);
2798 }
2799
2800 /* restore the signals */
2801 if (alrmwasblocked == 0)
2802 (void) sm_releasesignal(SIGALRM);
2803 if (chldwasblocked == 0)
2804 (void) sm_releasesignal(SIGCHLD);
2805 }
2806
2807 /*
2808 ** COUNT_OPEN_CONNECTIONS
2809 **
2810 ** Parameters:
2811 ** hostaddr - ClientAddress
2812 **
2813 ** Returns:
2814 ** the number of open connections for this client
2815 **
2816 */
2817
2818 int
2819 count_open_connections(hostaddr)
2820 SOCKADDR *hostaddr;
2821 {
2822 int i, n;
2823
2824 if (hostaddr == NULL)
2825 return 0;
2826
2827 /*
2828 ** This code gets called before proc_list_add() gets called,
2829 ** so we (the daemon child for this connection) have not yet
2830 ** counted ourselves. Hence initialize the counter to 1
2831 ** instead of 0 to compensate.
2832 */
2833
2834 n = 1;
2835 for (i = 0; i < ProcListSize; i++)
2836 {
2837 if (ProcListVec[i].proc_pid == NO_PID)
2838 continue;
2839 if (hostaddr->sa.sa_family !=
2840 ProcListVec[i].proc_hostaddr.sa.sa_family)
2841 continue;
2842 #if NETINET
2843 if (hostaddr->sa.sa_family == AF_INET &&
2844 (hostaddr->sin.sin_addr.s_addr ==
2845 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
2846 n++;
2847 #endif /* NETINET */
2848 #if NETINET6
2849 if (hostaddr->sa.sa_family == AF_INET6 &&
2850 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
2851 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
2852 n++;
2853 #endif /* NETINET6 */
2854 }
2855 return n;
2856 }