2 * Copyright (c) 1998-2007, 2009 Proofpoint, Inc. and its suppliers.
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.
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.
16 SM_RCSID("@(#)$Id: util.c,v 8.427 2013-11-22 20:51:57 ca Exp $")
18 #include <sm/sendmail.h>
21 # include <sm/ixlen.h>
25 ** NEWSTR -- Create a copy of a C string
28 ** s -- the string to copy. [A]
31 ** pointer to newly allocated string.
36 newstr_tagged(s, tag, line, group)
54 n = sm_malloc_tagged_x(l + 1, tag, line, group);
55 sm_strlcpy(n, s, l + 1);
58 #if SM_HEAP_CHECK <= 2
65 ** ADDQUOTES -- Adds quotes & quote bits to a string.
67 ** Runs through a string and adds backslashes and quote bits.
70 ** s -- the string to modify. [A]
71 ** rpool -- resource pool from which to allocate result
74 ** pointer to quoted string.
89 /* Find length of quoted string */
90 while ((c = *p++) != '\0')
93 if (c == '\\' || c == '"')
97 q = r = sm_rpool_malloc_x(rpool, len + 3);
100 /* add leading quote */
102 while ((c = *p++) != '\0')
105 if (c == '\\' || c == '"')
115 ** STRIPBACKSLASH -- Strip all leading backslashes from a string, provided
116 ** the following character is alpha-numerical.
117 ** This is done in place.
119 ** XXX: This may be a problem for EAI?
122 ** s -- the string to strip.
137 while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
146 ** RFC822_STRING -- Checks string for proper RFC822 string quoting.
148 ** Runs through a string and verifies RFC822 special characters
149 ** are only found inside comments, quoted strings, or backslash
150 ** escaped. Also verified balanced quotes and parenthesis.
152 ** XXX: This may be a problem for EAI? MustQuoteChars is used.
153 ** If this returns false, current callers just invoke addquotes().
156 ** s -- the string to modify. [A]
159 ** true iff the string is RFC822 compliant, false otherwise.
175 /* escaped character */
182 else if (commentlev == 0 && *c == '"')
196 else if (commentlev == 0 &&
197 strchr(MustQuoteChars, *c) != NULL)
203 /* unbalanced '"' or '(' */
204 return !quoted && commentlev == 0;
208 ** SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
210 ** Arbitrarily shorten (in place) an RFC822 string and rebalance
211 ** comments and quotes.
214 ** string -- the string to shorten [A]
215 ** length -- the maximum size, 0 if no maximum
218 ** true if string is changed, false otherwise
221 ** Changes string in place, possibly resulting
222 ** in a shorter string.
226 shorten_rfc822_string(string, length)
230 bool backslash = false;
231 bool modified = false;
238 ** If have to rebalance an already short enough string,
239 ** need to do it within allocated space.
242 slen = strlen(string);
243 if (length == 0 || slen < length)
256 else if (*ptr == '(')
261 else if (*ptr == ')')
263 if (--parencount < 0)
267 /* Inside a comment, quotes don't matter */
268 if (parencount <= 0 && *ptr == '"')
272 /* Check for sufficient space for next character */
273 if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
277 /* Not enough, backtrack */
280 else if (*ptr == '(' && !quoted)
282 else if (*ptr == '"' && parencount == 0)
290 while (parencount-- > 0)
317 ** FIND_CHARACTER -- find an unquoted character in an RFC822 string
319 ** Find an unquoted, non-commented character in an RFC822
320 ** string and return a pointer to its location in the string.
323 ** string -- the string to search [A]
324 ** character -- the character to find
327 ** pointer to the character, or
328 ** a pointer to the end of the line if character is not found
332 find_character(string, character)
336 bool backslash = false;
340 while (string != NULL && *string != '\0')
345 if (!quoted && character == '\\' && *string == '\\')
362 if (--parencount < 0)
367 /* Inside a comment, nothing matters */
376 else if (*string == character && !quoted)
381 /* Return pointer to the character */
386 ** CHECK_BODYTYPE -- check bodytype parameter
389 ** bodytype -- bodytype parameter
392 ** BODYTYPE_* according to parameter
397 check_bodytype(bodytype)
400 /* check body type for legality */
401 if (bodytype == NULL)
402 return BODYTYPE_NONE;
403 if (SM_STRCASEEQ(bodytype, "7bit"))
404 return BODYTYPE_7BIT;
405 if (SM_STRCASEEQ(bodytype, "8bitmime"))
406 return BODYTYPE_8BITMIME;
407 return BODYTYPE_ILLEGAL;
411 ** TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
414 ** str -- string to truncate [A]
415 ** len -- maximum length (including '\0') (0 for unlimited)
416 ** delim -- delimiter character
423 truncate_at_delim(str, len, delim)
430 if (str == NULL || len == 0 || strlen(str) < len)
433 *(str + len - 1) = '\0';
434 while ((p = strrchr(str, delim)) != NULL)
437 if (p - str + 4 < len)
441 (void) sm_strlcat(str, "...", len);
446 /* Couldn't find a place to append "..." */
448 (void) sm_strlcpy(str, "...", len);
454 ** XALLOC -- Allocate memory, raise an exception on error
457 ** sz -- size of area to allocate.
460 ** pointer to data region.
463 ** SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
466 ** Memory is allocated.
471 xalloc_tagged(sz, file, line)
475 #else /* SM_HEAP_CHECK */
478 #endif /* SM_HEAP_CHECK */
484 /* some systems can't handle size zero mallocs */
488 /* scaffolding for testing error handling code */
489 sm_xtrap_raise_x(&SmHeapOutOfMemory);
491 p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
494 sm_exc_raise_x(&SmHeapOutOfMemory);
500 ** COPYPLIST -- copy list of pointers.
502 ** This routine is the equivalent of strdup for lists of
506 ** list -- list of pointers to copy.
507 ** Must be NULL terminated.
508 ** copycont -- if true, copy the contents of the vector
509 ** (which must be a string) also.
510 ** rpool -- resource pool from which to allocate storage,
518 copyplist(list, copycont, rpool)
524 register char **newvp;
526 for (vp = list; *vp != NULL; vp++)
532 ** Hack: rpool is NULL if invoked from readcf(),
533 ** so "ignore" the allocation by setting group to 0.
536 newvp = (char **) sm_rpool_malloc_tagged_x(rpool,
537 (vp - list) * sizeof(*vp), "copyplist", 0,
538 NULL == rpool ? 0 : 1);
539 memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof(*vp));
543 for (vp = newvp; *vp != NULL; vp++)
544 *vp = sm_rpool_strdup_tagged_x(rpool, *vp,
545 "copyplist", 0, NULL == rpool ? 0 : 1);
552 ** COPYQUEUE -- copy address queue.
554 ** This routine is the equivalent of strdup for address queues;
555 ** addresses marked as QS_IS_DEAD() aren't copied
558 ** addr -- list of address structures to copy.
559 ** rpool -- resource pool from which to allocate storage
566 copyqueue(addr, rpool)
570 register ADDRESS *newaddr;
572 register ADDRESS **tail = &ret;
576 if (!QS_IS_DEAD(addr->q_state))
578 newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
580 STRUCTCOPY(*addr, *newaddr);
582 tail = &newaddr->q_next;
592 ** LOG_SENDMAIL_PID -- record sendmail pid and command line.
595 ** e -- the current envelope.
601 ** writes pidfile, logs command line.
602 ** keeps file open and locked to prevent overwrite of active file
605 static SM_FILE_T *Pidf = NULL;
612 char pidpath[MAXPATHLEN];
613 extern char *CommandLineArgs;
615 /* write the pid to the log file for posterity */
616 sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT|SFF_NBLOCK;
617 if (TrustedUid != 0 && RealUid == TrustedUid)
618 sff |= SFF_OPENASROOT;
619 expand(PidFile, pidpath, sizeof(pidpath), e);
620 Pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
623 if (errno == EWOULDBLOCK)
624 sm_syslog(LOG_ERR, NOQID,
625 "unable to write pid to %s: file in use by another process",
628 sm_syslog(LOG_ERR, NOQID,
629 "unable to write pid to %s: %s",
630 pidpath, sm_errstring(errno));
634 PidFilePid = getpid();
636 /* write the process id on line 1 */
637 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%ld\n",
640 /* line 2 contains all command line flags */
641 (void) sm_io_fprintf(Pidf, SM_TIME_DEFAULT, "%s\n",
645 (void) sm_io_flush(Pidf, SM_TIME_DEFAULT);
648 ** Leave pid file open until process ends
649 ** so it's not overwritten by another
654 sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
658 ** CLOSE_SENDMAIL_PID -- close sendmail pid file
673 (void) sm_io_close(Pidf, SM_TIME_DEFAULT);
678 ** SET_DELIVERY_MODE -- set and record the delivery mode
681 ** mode -- delivery mode
682 ** e -- the current envelope.
688 ** sets {deliveryMode} macro
692 set_delivery_mode(mode, e)
698 e->e_sendmode = (char) mode;
699 buf[0] = (char) mode;
701 macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
705 ** SET_OP_MODE -- set and record the op mode
709 ** e -- the current envelope.
715 ** sets {opMode} macro
723 extern ENVELOPE BlankEnvelope;
725 OpMode = (char) mode;
726 buf[0] = (char) mode;
728 macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
732 ** PRINTAV -- print argument vector.
735 ** fp -- output file pointer.
736 ** av -- argument vector.
758 for (cp = *av++; *cp != '\0'; cp++) {
759 v = (unsigned char)(*cp & 0x00ff);
760 (void) sm_io_putc(fp, SM_TIME_DEFAULT, v);
762 if (isascii(v) && isprint(v))
763 (void) sm_io_putc(fp, SM_TIME_DEFAULT,
766 (void) sm_io_fprintf(fp,
767 SM_TIME_DEFAULT, "\\x%hhx", v);
771 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
774 #endif /* _FFR_8BITENVADDR */
776 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
777 "\n\t%08lx=", (unsigned long) *av);
779 (void) sm_io_putc(fp, SM_TIME_DEFAULT, ' ');
781 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
782 "%s", str2prt(*av++));
787 /* don't print this if invoked directly (not via xputs())? */
788 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\n');
792 ** XPUTS -- put string doing control escapes.
795 ** fp -- output file pointer.
796 ** s -- string to put. [A]
812 bool shiftout = false;
813 extern struct metamac MetaMacros[];
814 static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
815 "@(#)$Debug: ANSI - enable reverse video in debug output $");
829 ** TermEscape is set here, rather than in main(),
830 ** because ANSI mode can be turned on or off at any time
831 ** if we are in -bt rule testing mode.
834 if (sm_debug_unknown(&DebugANSI))
836 if (sm_debug_active(&DebugANSI, 1))
838 TermEscape.te_rv_on = "\033[7m";
839 TermEscape.te_normal = "\033[0m";
843 TermEscape.te_rv_on = "";
844 TermEscape.te_normal = "";
850 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s<null>%s",
851 TermEscape.te_rv_on, TermEscape.te_normal);
854 while ((c = (*s++ & 0377)) != '\0')
858 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
859 TermEscape.te_normal);
862 if (!isascii(c) && !tTd(84, 1))
866 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
868 TermEscape.te_rv_on);
875 if (c == MACROEXPAND || c == MACRODEXPAND)
877 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
879 TermEscape.te_rv_on);
880 if (c == MACRODEXPAND)
881 (void) sm_io_putc(fp,
882 SM_TIME_DEFAULT, '&');
886 if (strchr("=~&?", *s) != NULL)
887 (void) sm_io_putc(fp,
890 if (bitset(0200, *s))
891 (void) sm_io_fprintf(fp,
894 macname(bitidx(*s++)));
896 (void) sm_io_fprintf(fp,
902 for (mp = MetaMacros; mp->metaname != '\0'; mp++)
904 if (bitidx(mp->metaval) == c)
906 (void) sm_io_fprintf(fp,
915 if (c == MATCHCLASS || c == MATCHNCLASS)
917 if (bitset(0200, *s))
918 (void) sm_io_fprintf(fp,
921 macname(bitidx(*s++)));
923 (void) sm_io_fprintf(fp,
928 if (mp->metaname != '\0')
931 /* unrecognized meta character */
932 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%sM-",
933 TermEscape.te_rv_on);
938 if (isascii(c) && isprint(c))
940 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
944 /* wasn't a meta-macro -- find another way to print it */
961 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
962 TermEscape.te_rv_on);
965 if (isascii(c) && isprint(c))
967 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '\\');
968 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c);
971 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %o ", c);
973 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " %#x ", c);
974 else if (!isascii(c) && !tTd(84, 1))
976 (void) sm_io_putc(fp, SM_TIME_DEFAULT, '^');
977 (void) sm_io_putc(fp, SM_TIME_DEFAULT, c ^ 0100);
981 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s",
982 TermEscape.te_normal);
983 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
987 ** MAKELOWER_A -- Translate a line into lower case
990 ** pp -- pointer to the string to translate (modified in place if possible). [A]
991 ** rpool -- rpool to use to reallocate string if needed
992 ** (if NULL: uses sm_malloc() internally)
995 ** lower cased string
998 ** String pointed to by pp is translated to lower case if possible.
1002 makelower_a(pp, rpool)
1009 SM_REQUIRE(pp != NULL);
1016 if (!addr_is_ascii(p))
1020 new = sm_lowercase(p);
1023 *pp = sm_rpool_strdup_tagged_x(rpool, new, "makelower", 0, 1);
1026 #endif /* USE_EAI */
1027 for (; (c = *p) != '\0'; p++)
1028 if (isascii(c) && isupper(c))
1034 makelower: Optimization for EAI case?
1038 while ((ch = (unsigned char)*str) != '\0' && ch < 127)
1040 if (isascii(c) && isupper(c))
1046 handle UTF-8 case: invoke sm_lowercase() etc
1050 ** MAKELOWER_BUF -- Translate a line into lower case
1053 ** str -- string to translate. [A]
1054 ** buf -- where to place lower case version.
1055 ** buflen -- size of buf
1061 ** String pointed to by str is translated to lower case if possible.
1064 ** if str is lower cased in place and str == buf (same pointer),
1065 ** then it is not explicitly copied.
1069 makelower_buf(str, buf, buflen)
1076 SM_REQUIRE(buf != NULL);
1079 lower = makelower_a(&str, NULL);
1080 if (lower != str || str != buf)
1082 sm_strlcpy(buf, lower, buflen);
1090 ** FIXCRLF -- fix CRLF in line.
1092 ** XXX: Could this be a problem for EAI? That is, can there
1093 ** be a string with \n and the previous octet is \n
1094 ** but is part of a UTF8 "char"?
1096 ** Looks for the CRLF combination and turns it into the
1097 ** UNIX canonical LF character. It only takes one line,
1098 ** i.e., it is assumed that the first LF found is the end
1102 ** line -- the line to fix. [A]
1103 ** stripnl -- if true, strip the newline also.
1109 ** line is changed in place.
1113 fixcrlf(line, stripnl)
1119 p = strchr(line, '\n');
1122 if (p > line && p[-1] == '\r')
1130 ** PUTLINE -- put a line like fputs obeying SMTP conventions
1132 ** This routine always guarantees outputting a newline (or CRLF,
1133 ** as appropriate) at the end of the string.
1136 ** l -- line to put. (should be [x])
1137 ** mci -- the mailer connection information.
1140 ** true iff line was written successfully
1143 ** output of l to mci->mci_out.
1151 return putxline(l, strlen(l), mci, PXLF_MAPFROM);
1155 ** PUTXLINE -- putline with flags bits.
1157 ** This routine always guarantees outputting a newline (or CRLF,
1158 ** as appropriate) at the end of the string.
1161 ** l -- line to put. (should be [x])
1162 ** len -- the length of the line.
1163 ** mci -- the mailer connection information.
1164 ** pxflags -- flag bits:
1165 ** PXLF_MAPFROM -- map From_ to >From_.
1166 ** PXLF_STRIP8BIT -- strip 8th bit.
1167 ** PXLF_HEADER -- map bare newline in header to newline space.
1168 ** PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
1169 ** PXLF_STRIPMQUOTE -- strip METAQUOTE bytes.
1172 ** true iff line was written successfully
1175 ** output of l to mci->mci_out.
1178 #define PUTX(limit) \
1181 quotenext = false; \
1184 unsigned char c = (unsigned char) *l++; \
1186 if (bitset(PXLF_STRIPMQUOTE, pxflags) && \
1187 !quotenext && c == METAQUOTE) \
1192 quotenext = false; \
1195 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, \
1201 if (TrafficLogFile != NULL) \
1202 (void) sm_io_putc(TrafficLogFile, \
1209 putxline(l, len, mci, pxflags)
1215 register char *p, *end;
1217 bool dead, quotenext, strip8bit;
1219 /* strip out 0200 bits -- these can look like TELNET protocol */
1220 strip8bit = bitset(MCIF_7BIT, mci->mci_flags) ||
1221 bitset(PXLF_STRIP8BIT, pxflags);
1230 /* find the end of the line */
1231 p = memchr(l, '\n', end - l);
1238 if (TrafficLogFile != NULL)
1239 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1240 "%05d >>> ", (int) CurrentPid);
1242 /* check for line overflow */
1243 while (mci->mci_mailer->m_linelimit > 0 &&
1244 (p - l + slop) > mci->mci_mailer->m_linelimit)
1246 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
1248 if (l[0] == '.' && slop == 0 &&
1249 bitnset(M_XDOT, mci->mci_mailer->m_flags))
1251 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1254 if (TrafficLogFile != NULL)
1255 (void) sm_io_putc(TrafficLogFile,
1256 SM_TIME_DEFAULT, '.');
1258 else if (l[0] == 'F' && slop == 0 &&
1259 bitset(PXLF_MAPFROM, pxflags) &&
1260 strncmp(l, "From ", 5) == 0 &&
1261 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1263 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1266 if (TrafficLogFile != NULL)
1267 (void) sm_io_putc(TrafficLogFile,
1278 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1279 '!') == SM_IO_EOF ||
1280 sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1281 mci->mci_mailer->m_eol) == SM_IO_EOF ||
1282 sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1288 if (TrafficLogFile != NULL)
1290 (void) sm_io_fprintf(TrafficLogFile,
1301 /* output last part */
1302 if (l[0] == '.' && slop == 0 &&
1303 bitnset(M_XDOT, mci->mci_mailer->m_flags) &&
1304 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1306 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1312 if (TrafficLogFile != NULL)
1313 (void) sm_io_putc(TrafficLogFile,
1314 SM_TIME_DEFAULT, '.');
1316 else if (l[0] == 'F' && slop == 0 &&
1317 bitset(PXLF_MAPFROM, pxflags) &&
1318 strncmp(l, "From ", 5) == 0 &&
1319 bitnset(M_ESCFROM, mci->mci_mailer->m_flags) &&
1320 !bitset(MCIF_INLONGLINE, mci->mci_flags))
1322 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1328 if (TrafficLogFile != NULL)
1329 (void) sm_io_putc(TrafficLogFile,
1330 SM_TIME_DEFAULT, '>');
1336 if (TrafficLogFile != NULL)
1337 (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1339 if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol))
1341 mci->mci_flags &= ~MCIF_INLONGLINE;
1342 if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1343 mci->mci_mailer->m_eol) == SM_IO_EOF)
1350 mci->mci_flags |= MCIF_INLONGLINE;
1352 if (l < end && *l == '\n')
1354 if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1355 bitset(PXLF_HEADER, pxflags))
1357 if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1364 if (TrafficLogFile != NULL)
1365 (void) sm_io_putc(TrafficLogFile,
1366 SM_TIME_DEFAULT, ' ');
1375 ** XUNLINK -- unlink a file, doing logging as appropriate.
1378 ** f -- name of file to unlink.
1381 ** return value of unlink()
1395 sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1399 if (i < 0 && LogLevel > 97)
1400 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1409 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1412 ** buf -- place to put the input line.
1413 ** (can be [A], but should be [x])
1414 ** siz -- size of buf.
1415 ** fp -- file to read from.
1416 ** timeout -- the timeout before error occurs.
1417 ** during -- what we are trying to read (for error messages).
1420 ** NULL on error (including timeout). This may also leave
1421 ** buf containing a null string.
1426 sfgets(buf, siz, fp, timeout, during)
1434 int save_errno, io_timeout, l;
1436 SM_REQUIRE(siz > 0);
1437 SM_REQUIRE(buf != NULL);
1450 /* convert the timeout to sm_io notation */
1451 io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1452 while (!sm_io_eof(fp) && !sm_io_error(fp))
1455 l = sm_io_fgets(fp, io_timeout, buf, siz);
1456 if (l < 0 && errno == EAGAIN)
1458 /* The sm_io_fgets() call timedout */
1460 sm_syslog(LOG_NOTICE, CurEnv->e_id,
1461 "timeout waiting for input from %.100s during %s",
1466 if (TrafficLogFile != NULL)
1467 (void) sm_io_fprintf(TrafficLogFile,
1469 "%05d <<< [TIMEOUT]\n",
1474 if (l >= 0 || errno != EINTR)
1476 (void) sm_io_clearerr(fp);
1480 /* clean up the books and exit */
1485 if (TrafficLogFile != NULL)
1486 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1492 if (TrafficLogFile != NULL)
1493 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1494 "%05d <<< %s", (int) CurrentPid, buf);
1497 for (p = buf; *p != '\0'; p++)
1500 else if (!HasEightBits)
1502 for (p = buf; *p != '\0'; p++)
1504 if (bitset(0200, *p))
1506 HasEightBits = true;
1515 ** FGETFOLDED -- like fgets, but knows about folded lines.
1518 ** buf -- place to put result.
1519 ** (can be [A], but should be [x])
1520 ** np -- pointer to bytes available; will be updated with
1521 ** the actual buffer size (not number of bytes filled)
1523 ** f -- file to read from.
1526 ** input line(s) on success, NULL on error or SM_IO_EOF.
1527 ** This will normally be buf -- unless the line is too
1528 ** long, when it will be sm_malloc_x()ed.
1531 ** buf gets lines from f, with continuation lines (lines
1532 ** with leading white space) appended. CRLF's are mapped
1533 ** into single newlines. Any trailing LF is stripped.
1534 ** Increases LineNumber for each line.
1538 fgetfolded(buf, np, f)
1543 register char *p = buf;
1548 SM_REQUIRE(np != NULL);
1551 SM_REQUIRE(buf != NULL);
1560 while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1564 i = sm_io_getc(f, SM_TIME_DEFAULT);
1568 (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1575 /* allocate new space */
1580 if (nn < MEMCHUNKSIZE)
1584 nbp = sm_malloc_x(nn);
1585 memmove(nbp, bp, p - bp);
1597 i = sm_io_getc(f, SM_TIME_DEFAULT);
1599 (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1600 if (i != ' ' && i != '\t')
1613 ** CURTIME -- return current time.
1619 ** the current time.
1632 ** ATOBOOL -- convert a string representation to boolean.
1634 ** Defaults to false
1637 ** s -- string to convert. Takes "tTyY", empty, and NULL as true,
1641 ** A boolean representation of the string.
1648 if (SM_IS_EMPTY(s) || strchr("tTyY", *s) != NULL)
1654 ** ATOOCT -- convert a string representation to octal.
1657 ** s -- string to convert.
1660 ** An integer representing the string interpreted as an
1670 while (*s >= '0' && *s <= '7')
1671 i = (i << 3) | (*s++ - '0');
1676 ** BITINTERSECT -- tell if two bitmaps intersect
1679 ** a, b -- the bitmaps in question
1682 ** true if they have a non-null intersection
1693 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1695 if ((a[i] & b[i]) != 0)
1702 ** BITZEROP -- tell if a bitmap is all zero
1705 ** map -- the bit map to check
1708 ** true if map is all zero.
1709 ** false if there are any bits set in map.
1718 for (i = BITMAPBYTES / sizeof(int); --i >= 0; )
1727 ** STRCONTAINEDIN -- tell if one string is contained in another
1730 ** icase -- ignore case?
1731 ** a -- possible substring. [A]
1732 ** b -- possible superstring. [A]
1733 ** (both must be the same format: X or Q)
1736 ** true if a is contained in b (case insensitive).
1741 strcontainedin(icase, a, b)
1753 if (icase && isascii(c) && isupper(c))
1755 for (; lb-- >= la; b++)
1760 isascii(*b) && isupper(*b) && tolower(*b) != c)
1762 if (sm_strncasecmp(a, b, la) == 0)
1769 if (strncmp(a, b, la) == 0)
1778 ** CHECKFD012 -- check low numbered file descriptors
1780 ** File descriptors 0, 1, and 2 should be open at all times.
1781 ** This routine verifies that, and fixes it if not true.
1784 ** where -- a tag printed if the assertion failed
1796 for (i = 0; i < 3; i++)
1801 ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1804 ** fd -- file descriptor to check.
1805 ** where -- tag to print on failure.
1812 checkfdopen(fd, where)
1818 if (fstat(fd, &st) < 0 && errno == EBADF)
1820 syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1827 ** CHECKFDS -- check for new or missing file descriptors
1830 ** where -- tag for printing. If null, take a base line.
1836 ** If where is set, shows changes since the last call.
1845 bool printhdr = true;
1846 int save_errno = errno;
1847 static BITMAP256 baseline;
1848 extern int DtableSize;
1850 if (DtableSize > BITMAPBITS)
1855 clrbitmap(baseline);
1857 for (fd = 0; fd < maxfd; fd++)
1861 if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1863 if (!bitnset(fd, baseline))
1865 clrbitn(fd, baseline);
1867 else if (!bitnset(fd, baseline))
1868 setbitn(fd, baseline);
1872 /* file state has changed */
1877 sm_syslog(LOG_DEBUG, CurEnv->e_id,
1882 dumpfd(fd, true, true);
1888 ** PRINTOPENFDS -- print the open file descriptors (for debugging)
1891 ** logit -- if set, send output to syslog; otherwise
1892 ** print for debugging.
1898 #if NETINET || NETINET6
1899 # include <arpa/inet.h>
1907 extern int DtableSize;
1909 for (fd = 0; fd < DtableSize; fd++)
1910 dumpfd(fd, false, logit);
1914 ** DUMPFD -- dump a file descriptor
1917 ** fd -- the file descriptor to dump.
1918 ** printclosed -- if set, print a notification even if
1919 ** it is closed; otherwise print nothing.
1920 ** logit -- if set, use sm_syslog instead of sm_dprintf()
1927 dumpfd(fd, printclosed, logit)
1937 auto SOCKADDR_LEN_T slen;
1947 (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1960 (void) sm_snprintf(p, SPACELEFT(buf, p),
1962 sm_errstring(errno));
1965 else if (printclosed)
1967 (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1973 i = fcntl(fd, F_GETFL, 0);
1976 (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1980 (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1981 (unsigned int) st.st_mode);
1983 switch (st.st_mode & S_IFMT)
1987 (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1989 memset(&sa, '\0', sizeof(sa));
1991 if (getsockname(fd, &sa.sa, &slen) < 0)
1992 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1993 sm_errstring(errno));
1996 hp = hostnamebyanyaddr(&sa);
2003 else if (sa.sa.sa_family == AF_INET)
2004 (void) sm_snprintf(p, SPACELEFT(buf, p),
2005 "%s/%d", hp, ntohs(sa.sin.sin_port));
2008 else if (sa.sa.sa_family == AF_INET6)
2009 (void) sm_snprintf(p, SPACELEFT(buf, p),
2010 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
2013 (void) sm_snprintf(p, SPACELEFT(buf, p),
2017 (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
2020 if (getpeername(fd, &sa.sa, &slen) < 0)
2021 (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
2022 sm_errstring(errno));
2025 hp = hostnamebyanyaddr(&sa);
2032 else if (sa.sa.sa_family == AF_INET)
2033 (void) sm_snprintf(p, SPACELEFT(buf, p),
2034 "%s/%d", hp, ntohs(sa.sin.sin_port));
2037 else if (sa.sa.sa_family == AF_INET6)
2038 (void) sm_snprintf(p, SPACELEFT(buf, p),
2039 "%s/%d", hp, ntohs(sa.sin6.sin6_port));
2042 (void) sm_snprintf(p, SPACELEFT(buf, p),
2046 #endif /* S_IFSOCK */
2049 (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
2055 (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
2060 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
2062 (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
2069 (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
2076 (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
2083 (void) sm_snprintf(p, SPACELEFT(buf, p),
2084 "dev=%ld/%ld, ino=%llu, nlink=%d, u/gid=%ld/%ld, ",
2085 (long) major(st.st_dev), (long) minor(st.st_dev),
2086 (ULONGLONG_T) st.st_ino,
2087 (int) st.st_nlink, (long) st.st_uid,
2090 (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
2091 (ULONGLONG_T) st.st_size);
2097 sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
2100 sm_dprintf("%s\n", buf);
2104 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname.
2107 ** host -- the host to shorten (stripped in place).
2108 ** [EAI: matched against $m: must be same format;
2109 ** conversion needed?]
2112 ** place where string was truncated, NULL if not truncated.
2116 shorten_hostname(host)
2124 /* strip off final dot */
2126 p = &host[(i == 0) ? 0 : i - 1];
2133 /* see if there is any domain at all -- if not, we are done */
2134 p = strchr(host, '.');
2138 /* yes, we have a domain -- see if it looks like us */
2139 mydom = macvalue('m', CurEnv);
2143 if ((canon ? sm_strcasecmp(p, mydom)
2144 : sm_strncasecmp(p, mydom, i)) == 0 &&
2145 (mydom[i] == '.' || mydom[i] == '\0'))
2154 ** PROG_OPEN -- open a program for reading
2157 ** argv -- the argument list.
2158 ** pfd -- pointer to a place to store the file descriptor.
2159 ** e -- the current envelope.
2162 ** pid of the process -- -1 if it failed.
2166 prog_open(argv, pfd, e)
2177 char buf[MAXPATHLEN];
2178 extern int DtableSize;
2182 syserr("%s: cannot create pipe for stdout", argv[0]);
2188 syserr("%s: cannot fork", argv[0]);
2189 (void) close(fdv[0]);
2190 (void) close(fdv[1]);
2196 (void) close(fdv[1]);
2201 /* Reset global flags */
2202 RestartRequest = NULL;
2203 RestartWorkGroup = false;
2204 ShutdownRequest = NULL;
2206 CurrentPid = getpid();
2209 ** Initialize exception stack and default exception
2210 ** handler for child process.
2213 sm_exc_newthread(fatal_error);
2215 /* child -- close stdin */
2218 /* stdout goes back to parent */
2219 (void) close(fdv[0]);
2220 if (dup2(fdv[1], 1) < 0)
2222 syserr("%s: cannot dup2 for stdout", argv[0]);
2225 (void) close(fdv[1]);
2227 /* stderr goes to transcript if available */
2228 if (e->e_xfp != NULL)
2232 xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
2233 if (xfd >= 0 && dup2(xfd, 2) < 0)
2235 syserr("%s: cannot dup2 for stderr", argv[0]);
2240 /* this process has no right to the queue file */
2241 if (e->e_lockfp != NULL)
2245 fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL);
2249 syserr("%s: lockfp does not have a fd", argv[0]);
2252 /* chroot to the program mailer directory, if defined */
2253 if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
2255 expand(ProgMailer->m_rootdir, buf, sizeof(buf), e);
2256 if (chroot(buf) < 0)
2258 syserr("prog_open: cannot chroot(%s)", buf);
2263 syserr("prog_open: cannot chdir(/)");
2268 /* run as default user */
2270 sm_mbdb_terminate();
2272 (void) sm_memstat_close();
2274 if (setgid(DefGid) < 0 && geteuid() == 0)
2276 syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2279 if (setuid(DefUid) < 0 && geteuid() == 0)
2281 syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2285 /* run in some directory */
2286 if (ProgMailer != NULL)
2287 p = ProgMailer->m_execdir;
2290 for (; p != NULL; p = q)
2295 expand(p, buf, sizeof(buf), e);
2298 if (buf[0] != '\0' && chdir(buf) >= 0)
2303 /* backup directories */
2304 if (chdir("/tmp") < 0)
2308 /* Check safety of program to be run */
2309 sff = SFF_ROOTOK|SFF_EXECOK;
2310 if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2311 sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2312 if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2313 sff |= SFF_NOPATHCHECK;
2315 sff |= SFF_SAFEDIRPATH;
2316 ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2318 sm_syslog(LOG_INFO, e->e_id,
2319 "Warning: prog_open: program %s unsafe: %s",
2320 argv[0], sm_errstring(ret));
2322 /* arrange for all the files to be closed */
2323 sm_close_on_exec(STDERR_FILENO + 1, DtableSize);
2325 /* now exec the process */
2326 (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2330 syserr("%s: cannot exec", argv[0]);
2331 if (transienterror(save_errno))
2334 return -1; /* avoid compiler warning on IRIX */
2338 ** GET_COLUMN -- look up a Column in a line buffer
2341 ** line -- the raw text line to search. [A]
2342 ** col -- the column number to fetch.
2343 ** delim -- the delimiter between columns. If null,
2345 ** buf -- the output buffer.
2346 ** buflen -- the length of buf.
2349 ** buf if successful.
2354 get_column(line, col, delim, buf, buflen)
2366 if ((char) delim == '\0')
2367 (void) sm_strlcpy(delimbuf, "\n\t ", sizeof(delimbuf));
2370 delimbuf[0] = (char) delim;
2376 return NULL; /* line empty */
2377 if (*p == (char) delim && col == 0)
2378 return NULL; /* first column empty */
2382 if (col == 0 && (char) delim == '\0')
2384 while (*begin != '\0' && SM_ISSPACE(*begin))
2388 for (i = 0; i < col; i++)
2390 if ((begin = strpbrk(begin, delimbuf)) == NULL)
2391 return NULL; /* no such column */
2393 if ((char) delim == '\0')
2395 while (*begin != '\0' && SM_ISSPACE(*begin))
2400 end = strpbrk(begin, delimbuf);
2407 (void) sm_strlcpy(buf, begin, i + 1);
2412 ** CLEANSTRCPY -- copy string keeping out bogus characters
2413 ** XXX: This may be a problem for EAI?
2416 ** t -- "to" string.
2417 ** f -- "from" string. [A]
2418 ** l -- length of space available in "to" string.
2425 cleanstrcpy(t, f, l)
2430 /* check for newlines and log if necessary */
2431 (void) denlstring(f, true, true);
2434 syserr("!cleanstrcpy: length == 0");
2437 while (l > 0 && *f != '\0')
2440 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2451 ** DENLSTRING -- convert newlines in a string to spaces
2454 ** s -- the input string [A]
2455 ** strict -- if set, don't permit continuation lines.
2456 ** logattacks -- if set, log attempted attacks.
2459 ** A pointer to a version of the string with newlines
2460 ** mapped to spaces. This should be copied.
2464 denlstring(s, strict, logattacks)
2471 static char *bp = NULL;
2475 while ((p = strchr(p, '\n')) != NULL)
2476 if (strict || (*++p != ' ' && *p != '\t'))
2484 /* allocate more space */
2485 char *nbp = sm_pmalloc_x(l);
2492 (void) sm_strlcpy(bp, s, l);
2493 for (p = bp; (p = strchr(p, '\n')) != NULL; )
2498 sm_syslog(LOG_NOTICE, CurEnv ? CurEnv->e_id : NULL,
2499 "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2500 RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2501 shortenstring(bp, MAXSHORTSTR));
2508 ** STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2511 ** s -- string to manipulate (in place) [A]
2512 ** c -- character to use as replacement
2515 ** true iff string did not contain "unprintable" characters
2530 if (!(isascii(*s) && isprint(*s)))
2541 ** PATH_IS_DIR -- check to see if file exists and is a directory.
2543 ** There are some additional checks for security violations in
2544 ** here. This routine is intended to be used for the host status
2548 ** pathname -- pathname to check for directory-ness. [x]
2549 ** createflag -- if set, create directory if needed.
2552 ** true -- if the indicated pathname is a directory
2553 ** false -- otherwise
2557 path_is_dir(pathname, createflag)
2561 struct stat statbuf;
2564 if (lstat(pathname, &statbuf) < 0)
2566 if (stat(pathname, &statbuf) < 0)
2569 if (errno != ENOENT || !createflag)
2571 if (mkdir(pathname, 0755) < 0)
2575 if (!S_ISDIR(statbuf.st_mode))
2581 /* security: don't allow writable directories */
2582 if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2591 ** PROC_LIST_ADD -- add process id to list of our children
2594 ** pid -- pid to add to list.
2595 ** task -- task of pid.
2596 ** type -- type of process.
2597 ** count -- number of processes.
2598 ** other -- other information for this type.
2604 ** May increase CurChildren. May grow ProcList.
2607 typedef struct procs PROCS_T;
2616 SOCKADDR proc_hostaddr;
2619 static PROCS_T *volatile ProcListVec = NULL;
2620 static int ProcListSize = 0;
2623 proc_list_add(pid, task, type, count, other, hostaddr)
2633 for (i = 0; i < ProcListSize; i++)
2635 if (ProcListVec[i].proc_pid == NO_PID)
2638 if (i >= ProcListSize)
2640 /* probe the existing vector to avoid growing infinitely */
2643 /* now scan again */
2644 for (i = 0; i < ProcListSize; i++)
2646 if (ProcListVec[i].proc_pid == NO_PID)
2650 if (i >= ProcListSize)
2652 /* grow process list */
2656 SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2657 npv = (PROCS_T *) sm_pmalloc_x((sizeof(*npv)) *
2658 (ProcListSize + PROC_LIST_SEG));
2660 /* Block SIGCHLD so reapchild() doesn't mess with us */
2661 chldwasblocked = sm_blocksignal(SIGCHLD);
2662 if (ProcListSize > 0)
2664 memmove(npv, ProcListVec,
2665 ProcListSize * sizeof(PROCS_T));
2666 sm_free(ProcListVec);
2669 /* XXX just use memset() to initialize this part? */
2670 for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2672 npv[i].proc_pid = NO_PID;
2673 npv[i].proc_task = NULL;
2674 npv[i].proc_type = PROC_NONE;
2677 ProcListSize += PROC_LIST_SEG;
2679 if (chldwasblocked == 0)
2680 (void) sm_releasesignal(SIGCHLD);
2682 ProcListVec[i].proc_pid = pid;
2683 PSTRSET(ProcListVec[i].proc_task, task);
2684 ProcListVec[i].proc_type = type;
2685 ProcListVec[i].proc_count = count;
2686 ProcListVec[i].proc_other = other;
2687 if (hostaddr != NULL)
2688 ProcListVec[i].proc_hostaddr = *hostaddr;
2690 memset(&ProcListVec[i].proc_hostaddr, 0,
2691 sizeof(ProcListVec[i].proc_hostaddr));
2693 /* if process adding itself, it's not a child */
2694 if (pid != CurrentPid)
2696 SM_ASSERT(CurChildren < INT_MAX);
2702 ** PROC_LIST_SET -- set pid task in process list
2705 ** pid -- pid to set
2706 ** task -- task of pid
2713 proc_list_set(pid, task)
2719 for (i = 0; i < ProcListSize; i++)
2721 if (ProcListVec[i].proc_pid == pid)
2723 PSTRSET(ProcListVec[i].proc_task, task);
2730 ** PROC_LIST_DROP -- drop pid from process list
2733 ** pid -- pid to drop
2734 ** st -- process status
2735 ** other -- storage for proc_other (return).
2741 ** May decrease CurChildren, CurRunners, or
2742 ** set RestartRequest or ShutdownRequest.
2744 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2745 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2750 proc_list_drop(pid, st, other)
2756 int type = PROC_NONE;
2758 for (i = 0; i < ProcListSize; i++)
2760 if (ProcListVec[i].proc_pid == pid)
2762 ProcListVec[i].proc_pid = NO_PID;
2763 type = ProcListVec[i].proc_type;
2765 *other = ProcListVec[i].proc_other;
2766 if (CurChildren > 0)
2772 if (type == PROC_CONTROL && WIFEXITED(st))
2774 /* if so, see if we need to restart or shutdown */
2775 if (WEXITSTATUS(st) == EX_RESTART)
2776 RestartRequest = "control socket";
2777 else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2778 ShutdownRequest = "control socket";
2780 else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2781 ProcListVec[i].proc_other > -1)
2783 /* restart this persistent runner */
2784 mark_work_group_restart(ProcListVec[i].proc_other, st);
2786 else if (type == PROC_QUEUE)
2788 CurRunners -= ProcListVec[i].proc_count;
2790 /* CHK_CUR_RUNNERS() can't be used here: uses syslog() */
2797 ** PROC_LIST_CLEAR -- clear the process list
2806 ** Sets CurChildren to zero.
2814 /* start from 1 since 0 is the daemon itself */
2815 for (i = 1; i < ProcListSize; i++)
2816 ProcListVec[i].proc_pid = NO_PID;
2821 ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2830 ** May decrease CurChildren.
2841 chldwasblocked = sm_blocksignal(SIGCHLD);
2843 /* start from 1 since 0 is the daemon itself */
2844 for (i = 1; i < ProcListSize; i++)
2846 pid = ProcListVec[i].proc_pid;
2847 if (pid == NO_PID || pid == CurrentPid)
2849 if (kill(pid, 0) < 0)
2852 sm_syslog(LOG_DEBUG, CurEnv->e_id,
2853 "proc_list_probe: lost pid %d",
2854 (int) ProcListVec[i].proc_pid);
2855 ProcListVec[i].proc_pid = NO_PID;
2856 SM_FREE(ProcListVec[i].proc_task);
2858 if (ProcListVec[i].proc_type == PROC_QUEUE)
2860 CurRunners -= ProcListVec[i].proc_count;
2861 CHK_CUR_RUNNERS("proc_list_probe", i,
2862 ProcListVec[i].proc_count);
2872 if (CurChildren < 0)
2874 if (chldwasblocked == 0)
2875 (void) sm_releasesignal(SIGCHLD);
2876 if (LogLevel > 10 && children != CurChildren && CurrentPid == DaemonPid)
2878 sm_syslog(LOG_ERR, NOQID,
2879 "proc_list_probe: found %d children, expected %d",
2880 children, CurChildren);
2885 ** PROC_LIST_DISPLAY -- display the process list
2888 ** out -- output file pointer
2889 ** prefix -- string to output in front of each line.
2896 proc_list_display(out, prefix)
2902 for (i = 0; i < ProcListSize; i++)
2904 if (ProcListVec[i].proc_pid == NO_PID)
2907 (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2909 (int) ProcListVec[i].proc_pid,
2910 ProcListVec[i].proc_task != NULL ?
2911 ProcListVec[i].proc_task : "(unknown)",
2912 (OpMode == MD_SMTP ||
2913 OpMode == MD_DAEMON ||
2914 OpMode == MD_ARPAFTP) ? "\r" : "");
2919 ** PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2922 ** type -- type of process to signal
2923 ** signal -- the type of signal to send
2928 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2929 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2934 proc_list_signal(type, signal)
2941 pid_t mypid = getpid();
2943 /* block these signals so that we may signal cleanly */
2944 chldwasblocked = sm_blocksignal(SIGCHLD);
2945 alrmwasblocked = sm_blocksignal(SIGALRM);
2947 /* Find all processes of type and send signal */
2948 for (i = 0; i < ProcListSize; i++)
2950 if (ProcListVec[i].proc_pid == NO_PID ||
2951 ProcListVec[i].proc_pid == mypid)
2953 if (ProcListVec[i].proc_type != type)
2955 (void) kill(ProcListVec[i].proc_pid, signal);
2958 /* restore the signals */
2959 if (alrmwasblocked == 0)
2960 (void) sm_releasesignal(SIGALRM);
2961 if (chldwasblocked == 0)
2962 (void) sm_releasesignal(SIGCHLD);
2966 ** COUNT_OPEN_CONNECTIONS
2969 ** hostaddr - ClientAddress
2972 ** the number of open connections for this client
2977 count_open_connections(hostaddr)
2982 if (hostaddr == NULL)
2986 ** This code gets called before proc_list_add() gets called,
2987 ** so we (the daemon child for this connection) have not yet
2988 ** counted ourselves. Hence initialize the counter to 1
2989 ** instead of 0 to compensate.
2993 for (i = 0; i < ProcListSize; i++)
2995 if (ProcListVec[i].proc_pid == NO_PID)
2997 if (hostaddr->sa.sa_family !=
2998 ProcListVec[i].proc_hostaddr.sa.sa_family)
3001 if (hostaddr->sa.sa_family == AF_INET &&
3002 (hostaddr->sin.sin_addr.s_addr ==
3003 ProcListVec[i].proc_hostaddr.sin.sin_addr.s_addr))
3007 if (hostaddr->sa.sa_family == AF_INET6 &&
3008 IN6_ARE_ADDR_EQUAL(&(hostaddr->sin6.sin6_addr),
3009 &(ProcListVec[i].proc_hostaddr.sin6.sin6_addr)))
3018 ** XCONNECT -- get X-CONNECT info
3021 ** inchannel -- FILE to check
3024 ** >0 if X-CONNECT/PROXY was used successfully (D_XCNCT*)
3025 ** 0 if X-CONNECT/PROXY was not given
3031 ** HA proxy version 1:
3033 ** PROXY TCP[4|6] IPv[4|6]-src-addr IPv[4|6]-dst-addr src-port dst-port\r\n
3034 ** PROXY UNKNOWN ...
3036 ** "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"
3037 ** "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n"
3038 ** "PROXY UNKNOWN\r\n"
3043 SM_FILE_T *inchannel;
3046 char *p, *b, delim, inp[MAXINPLINE];
3049 char pvpbuf[PSBUFSIZE];
3050 char *peerhostname; /* name of SMTP peer or "localhost" */
3051 extern ENVELOPE BlankEnvelope;
3053 int haproxy = AF_UNSPEC;
3054 # define HAPROXY "PROXY "
3055 # define HAPROXYLEN (sizeof(HAPROXY) - 1)
3058 #define XCONNECT "X-CONNECT "
3059 #define XCNNCTLEN (sizeof(XCONNECT) - 1)
3061 /* Ask the ruleset whether to use x-connect */
3063 peerhostname = RealHostName;
3064 if (peerhostname == NULL)
3065 peerhostname = "localhost";
3066 r = rscap("x_connect", peerhostname,
3067 anynet_ntoa(&RealHostAddr), &BlankEnvelope,
3068 &pvp, pvpbuf, sizeof(pvpbuf));
3070 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
3071 if (r == EX_UNAVAILABLE)
3076 sm_syslog(LOG_INFO, NOQID, "x-connect: rscap=%d", r);
3079 if (pvp != NULL && pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET)
3081 /* $#: no x-connect */
3083 sm_syslog(LOG_INFO, NOQID, "x-connect: nope");
3088 if (pvp != NULL && pvp[0] != NULL && strcasecmp(pvp[0], "haproxy1") == 0)
3095 if (pvp != NULL && pvp[0] != NULL &&
3096 pvp[0][0] == '2' && pvp[0][1] == '2' && pvp[0][2] == '0')
3098 char *hostname; /* my hostname ($j) */
3100 hostname = macvalue('j', &BlankEnvelope);
3102 sm_syslog(LOG_INFO, NOQID, "x-connect=%s", pvp[0]);
3103 message("220-%s %s", hostname != NULL ? hostname : "xconnect",
3104 pvp[1] != NULL ? pvp[1] : "waiting for xconnect");
3105 sm_io_flush(OutChannel, SM_TIME_DEFAULT);
3109 p = sfgets(inp, sizeof(inp), InChannel, TimeOuts.to_nextcommand, "pre");
3111 sm_syslog(LOG_INFO, NOQID, "x-connect: input=%s", p);
3113 if (AF_UNSPEC != haproxy)
3115 if (p == NULL || strncasecmp(p, HAPROXY, HAPROXYLEN) != 0)
3118 # define HAPUNKNOWN "UNKNOWN"
3119 # define HAPUNKNOWNLEN (sizeof(HAPUNKNOWN) - 1)
3120 if (strncasecmp(p, HAPUNKNOWN, HAPUNKNOWNLEN) == 0)
3122 /* how to handle this? */
3123 sm_syslog(LOG_INFO, NOQID, "haproxy: input=%s, status=ignored", p);
3126 # define HAPTCP4 "TCP4 "
3127 # define HAPTCP6 "TCP6 "
3128 # define HAPTCPNLEN (sizeof(HAPTCP4) - 1)
3129 if (strncasecmp(p, HAPTCP4, HAPTCPNLEN) == 0)
3132 if (strncasecmp(p, HAPTCP6, HAPTCPNLEN) == 0)
3135 if (AF_LOCAL != haproxy)
3143 if (p == NULL || strncasecmp(p, XCONNECT, XCNNCTLEN) != 0)
3146 while (SM_ISSPACE(*p))
3152 /* parameters: IPAddress [Hostname[ M]] */
3154 while (*p != '\0' && isascii(*p) &&
3155 (isalnum(*p) || *p == '.' || *p== ':'))
3160 memset(&addr, '\0', sizeof(addr));
3161 addr.sin.sin_addr.s_addr = inet_addr(b);
3162 if (addr.sin.sin_addr.s_addr != INADDR_NONE)
3164 addr.sa.sa_family = AF_INET;
3165 memcpy(&RealHostAddr, &addr, sizeof(addr));
3167 sm_syslog(LOG_INFO, NOQID, "x-connect: addr=%s",
3168 anynet_ntoa(&RealHostAddr));
3171 else if ((r = inet_pton(AF_INET6, b, &addr.sin6.sin6_addr)) == 1)
3173 addr.sa.sa_family = AF_INET6;
3174 memcpy(&RealHostAddr, &addr, sizeof(addr));
3180 if (AF_UNSPEC != haproxy)
3183 ** dst-addr and dst-port are ignored because
3184 ** they are not really worth to check:
3185 ** IPv[4|6]-dst-addr: must be one of "our" addresses,
3186 ** dst-port: must be the DaemonPort.
3187 ** We also do not check whether
3188 ** haproxy == addr.sa.sa_family
3194 while (*p != '\0' && !SM_ISSPACE(*p))
3196 if (*p != '\0' && SM_ISSPACE(*p))
3200 unsigned short port;
3202 port = htons(atoi(p));
3203 if (AF_INET == haproxy)
3204 RealHostAddr.sin.sin_port = port;
3206 if (AF_INET6 == haproxy)
3207 RealHostAddr.sin6.sin6_port = port;
3211 SM_FREE(RealHostName);
3216 /* more parameters? */
3220 for (b = ++p, i = 0;
3221 *p != '\0' && isascii(*p) && (isalnum(*p) || *p == '.' || *p == '-');
3227 if (i > MAXNAME) /* EAI:ok */
3228 b[MAXNAME] = '\0'; /* EAI:ok */
3231 SM_FREE(RealHostName);
3232 RealHostName = newstr(b);
3234 sm_syslog(LOG_INFO, NOQID, "x-connect: host=%s", b);
3241 while (*p != '\0' && SM_ISSPACE(*p))
3248 e = strpbrk(p, "\r\n");
3251 sm_syslog(LOG_INFO, NOQID, "x-connect: rest=%s", p);
3258 #endif /* _FFR_XCNCT */