2 * Copyright (c) 1998-2003 Sendmail, 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: usersmtp.c,v 8.437.2.10 2003/05/05 23:51:47 ca Exp $")
21 extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int, bool));
22 static void datatimeout __P((void));
23 static void esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
24 static void helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
25 static int smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
28 extern void *sm_sasl_malloc __P((unsigned long));
29 extern void sm_sasl_free __P((void *));
33 ** USERSMTP -- run SMTP protocol from the user end.
35 ** This protocol is described in RFC821.
38 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */
39 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */
40 #define SMTPCLOSING 421 /* "Service Shutting Down" */
42 #define ENHSCN(e, d) ((e) == NULL ? (d) : (e))
44 #define ENHSCN_RPOOL(e, d, rpool) \
45 ((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
47 static char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
48 static char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
49 static bool SmtpNeedIntro; /* need "while talking" in transcript */
51 ** SMTPINIT -- initialize SMTP.
53 ** Opens the connection and sends the initial protocol.
56 ** m -- mailer to create connection to.
57 ** mci -- the mailer connection info.
59 ** onlyhelo -- send only helo command?
65 ** creates connection and sends initial protocol.
69 smtpinit(m, mci, e, onlyhelo)
84 sm_dprintf("smtpinit ");
89 ** Open the connection to the mailer.
93 CurHostName = mci->mci_host; /* XXX UGLY XXX */
94 if (CurHostName == NULL)
95 CurHostName = MyHostName;
97 state = mci->mci_state;
103 /* need to clear old information */
115 /* shouldn't happen */
120 syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
129 mci->mci_state = MCIS_OPENING;
133 ** Get the greeting message.
134 ** This should appear spontaneously. Give it five minutes to
138 SmtpPhase = mci->mci_phase = "client greeting";
139 sm_setproctitle(true, e, "%s %s: %s",
140 qid_printname(e), CurHostName, mci->mci_phase);
141 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
144 if (REPLYTYPE(r) == 4)
146 if (REPLYTYPE(r) != 2)
150 ** Send the HELO command.
151 ** My mother taught me to always introduce myself.
155 if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
156 mci->mci_flags |= MCIF_ESMTP;
157 hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
160 #if _FFR_IGNORE_EXT_ON_HELO
161 mci->mci_flags &= ~MCIF_HELO;
162 #endif /* _FFR_IGNORE_EXT_ON_HELO */
163 if (bitnset(M_LMTP, m->m_flags))
165 smtpmessage("LHLO %s", m, mci, hn);
166 SmtpPhase = mci->mci_phase = "client LHLO";
168 else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
169 !bitnset(M_FSMTP, m->m_flags))
171 smtpmessage("EHLO %s", m, mci, hn);
172 SmtpPhase = mci->mci_phase = "client EHLO";
176 smtpmessage("HELO %s", m, mci, hn);
177 SmtpPhase = mci->mci_phase = "client HELO";
178 #if _FFR_IGNORE_EXT_ON_HELO
179 mci->mci_flags |= MCIF_HELO;
180 #endif /* _FFR_IGNORE_EXT_ON_HELO */
182 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
183 CurHostName, mci->mci_phase);
185 bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
190 else if (REPLYTYPE(r) == 5)
192 if (bitset(MCIF_ESMTP, mci->mci_flags) &&
193 !bitnset(M_LMTP, m->m_flags))
195 /* try old SMTP instead */
196 mci->mci_flags &= ~MCIF_ESMTP;
201 else if (REPLYTYPE(r) != 2)
205 ** Check to see if we actually ended up talking to ourself.
206 ** This means we didn't know about an alias or MX, or we managed
207 ** to connect to an echo server.
210 p = strchr(&SmtpReplyBuffer[4], ' ');
213 if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
214 !bitnset(M_LMTP, m->m_flags) &&
215 sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
217 syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
219 mci_setstat(mci, EX_CONFIG, "5.3.5",
220 "553 5.3.5 system config error");
227 ** If this is expected to be another sendmail, send some internal
232 # if !_FFR_DEPRECATE_MAILER_FLAG_I
233 || bitnset(M_INTERNAL, m->m_flags)
234 # endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
235 # if _FFR_MSP_VERBOSE
236 /* If we're running as MSP, "propagate" -v flag if possible. */
237 || (UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
238 # endif /* _FFR_MSP_VERBOSE */
241 /* tell it to be verbose */
242 smtpmessage("VERB", m, mci);
243 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
248 if (mci->mci_state != MCIS_CLOSED)
250 mci->mci_state = MCIS_OPEN;
254 /* got a 421 error code during startup */
257 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
258 if (mci->mci_state != MCIS_CLOSED)
263 /* XXX should use code from other end iff ENHANCEDSTATUSCODES */
264 mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
266 if (mci->mci_state != MCIS_CLOSED)
271 mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
276 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
279 ** line -- the response line.
280 ** firstline -- set if this is the first line of the reply.
282 ** mci -- the mailer connection info.
283 ** e -- the envelope.
290 esmtp_check(line, firstline, m, mci, e)
297 if (strstr(line, "ESMTP") != NULL)
298 mci->mci_flags |= MCIF_ESMTP;
301 ** Dirty hack below. Quoting the author:
302 ** This was a response to people who wanted SMTP transmission to be
303 ** just-send-8 by default. Essentially, you could put this tag into
304 ** your greeting message to behave as though the F=8 flag was set on
308 if (strstr(line, "8BIT-OK") != NULL)
309 mci->mci_flags |= MCIF_8BITOK;
313 /* specify prototype so compiler can check calls */
314 static char *str_union __P((char *, char *, SM_RPOOL_T *));
317 ** STR_UNION -- create the union of two lists
320 ** s1, s2 -- lists of items (separated by single blanks).
321 ** rpool -- resource pool from which result is allocated.
324 ** the union of both lists.
328 str_union(s1, s2, rpool)
332 char *hr, *h1, *h, *res;
335 if (s1 == NULL || *s1 == '\0')
337 if (s2 == NULL || *s2 == '\0')
342 res = (char *) sm_rpool_malloc(rpool, rl + 2);
349 (void) sm_strlcpy(res, s1, rl);
354 /* walk through s2 */
355 while (h != NULL && *h1 != '\0')
357 /* is there something after the current word? */
358 if ((h = strchr(h1, ' ')) != NULL)
362 /* does the current word appear in s1 ? */
363 if (iteminlist(h1, s1, " ") == NULL)
365 /* add space as delimiter */
371 /* advance pointer in result list */
377 /* there are more items */
387 ** HELO_OPTIONS -- process the options on a HELO line.
390 ** line -- the response line.
391 ** firstline -- set if this is the first line of the reply.
393 ** mci -- the mailer connection info.
394 ** e -- the envelope (unused).
401 helo_options(line, firstline, m, mci, e)
409 #if _FFR_IGNORE_EXT_ON_HELO
410 static bool logged = false;
411 #endif /* _FFR_IGNORE_EXT_ON_HELO */
416 mci->mci_saslcap = NULL;
418 #if _FFR_IGNORE_EXT_ON_HELO
420 #endif /* _FFR_IGNORE_EXT_ON_HELO */
423 #if _FFR_IGNORE_EXT_ON_HELO
424 else if (bitset(MCIF_HELO, mci->mci_flags))
426 if (LogLevel > 8 && !logged)
428 sm_syslog(LOG_WARNING, NOQID,
429 "server=%s [%s] returned extensions despite HELO command",
430 macvalue(macid("{server_name}"), e),
431 macvalue(macid("{server_addr}"), e));
436 #endif /* _FFR_IGNORE_EXT_ON_HELO */
438 if (strlen(line) < 5)
441 p = strpbrk(line, " =");
444 if (sm_strcasecmp(line, "size") == 0)
446 mci->mci_flags |= MCIF_SIZE;
448 mci->mci_maxsize = atol(p);
450 else if (sm_strcasecmp(line, "8bitmime") == 0)
452 mci->mci_flags |= MCIF_8BITMIME;
453 mci->mci_flags &= ~MCIF_7BIT;
455 else if (sm_strcasecmp(line, "expn") == 0)
456 mci->mci_flags |= MCIF_EXPN;
457 else if (sm_strcasecmp(line, "dsn") == 0)
458 mci->mci_flags |= MCIF_DSN;
459 else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
460 mci->mci_flags |= MCIF_ENHSTAT;
461 else if (sm_strcasecmp(line, "pipelining") == 0)
462 mci->mci_flags |= MCIF_PIPELINED;
463 else if (sm_strcasecmp(line, "verb") == 0)
464 mci->mci_flags |= MCIF_VERB;
466 else if (sm_strcasecmp(line, "starttls") == 0)
467 mci->mci_flags |= MCIF_TLS;
468 #endif /* STARTTLS */
469 else if (sm_strcasecmp(line, "deliverby") == 0)
471 mci->mci_flags |= MCIF_DLVR_BY;
473 mci->mci_min_by = atol(p);
476 else if (sm_strcasecmp(line, "auth") == 0)
478 if (p != NULL && *p != '\0')
480 if (mci->mci_saslcap != NULL)
483 ** Create the union with previous auth
484 ** offerings because we recognize "auth "
485 ** and "auth=" (old format).
488 mci->mci_saslcap = str_union(mci->mci_saslcap,
490 mci->mci_flags |= MCIF_AUTH;
497 mci->mci_saslcap = (char *)
498 sm_rpool_malloc(mci->mci_rpool, l);
499 if (mci->mci_saslcap != NULL)
501 (void) sm_strlcpy(mci->mci_saslcap, p,
503 mci->mci_flags |= MCIF_AUTH;
512 static int getsimple __P((void *, int, const char **, unsigned *));
513 static int getsecret __P((sasl_conn_t *, void *, int, sasl_secret_t **));
514 static int saslgetrealm __P((void *, int, const char **, const char **));
515 static int readauth __P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
516 static int getauth __P((MCI *, ENVELOPE *, SASL_AI_T *));
517 static char *removemech __P((char *, char *, SM_RPOOL_T *));
518 static int attemptauth __P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
520 static sasl_callback_t callbacks[] =
522 { SASL_CB_GETREALM, &saslgetrealm, NULL },
523 #define CB_GETREALM_IDX 0
524 { SASL_CB_PASS, &getsecret, NULL },
525 #define CB_PASS_IDX 1
526 { SASL_CB_USER, &getsimple, NULL },
527 #define CB_USER_IDX 2
528 { SASL_CB_AUTHNAME, &getsimple, NULL },
529 #define CB_AUTHNAME_IDX 3
530 { SASL_CB_VERIFYFILE, &safesaslfile, NULL },
531 #define CB_SAFESASL_IDX 4
532 { SASL_CB_LIST_END, NULL, NULL }
536 ** INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
542 ** SASL_OK -- if successful.
543 ** SASL error code -- otherwise.
546 ** checks/sets sasl_clt_init.
549 static bool sasl_clt_init = false;
558 result = sasl_client_init(callbacks);
560 /* should we retry later again or just remember that it failed? */
561 if (result == SASL_OK)
562 sasl_clt_init = true;
566 ** STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
575 ** checks/sets sasl_clt_init.
583 sasl_clt_init = false;
587 ** GETSASLDATA -- process the challenges from the SASL protocol
589 ** This gets the relevant sasl response data out of the reply
593 ** line -- the response line.
594 ** firstline -- set if this is the first line of the reply.
596 ** mci -- the mailer connection info.
597 ** e -- the envelope (unused).
603 static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
606 getsasldata(line, firstline, m, mci, e)
617 # endif /* SASL < 20000 */
619 /* if not a continue we don't care about it */
623 !isascii(line[1]) || !isdigit(line[1]) ||
624 !isascii(line[2]) || !isdigit(line[2]))
626 SM_FREE_CLR(mci->mci_sasl_string);
630 /* forget about "334 " */
634 /* XXX put this into a macro/function? It's duplicated below */
635 if (mci->mci_sasl_string != NULL)
637 if (mci->mci_sasl_string_len <= len)
639 sm_free(mci->mci_sasl_string); /* XXX */
640 mci->mci_sasl_string = xalloc(len + 1);
644 mci->mci_sasl_string = xalloc(len + 1);
646 result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
647 (unsigned int *) &mci->mci_sasl_string_len);
648 if (result != SASL_OK)
650 mci->mci_sasl_string_len = 0;
651 *mci->mci_sasl_string = '\0';
653 # else /* SASL >= 20000 */
654 out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
655 result = sasl_decode64(line, len, out, (unsigned int *) &len);
656 if (result != SASL_OK)
663 ** mci_sasl_string is "shared" with Cyrus-SASL library; hence
664 ** it can't be in an rpool unless we use the same memory
665 ** management mechanism (with same rpool!) for Cyrus SASL.
668 if (mci->mci_sasl_string != NULL)
670 if (mci->mci_sasl_string_len <= len)
672 sm_free(mci->mci_sasl_string); /* XXX */
673 mci->mci_sasl_string = xalloc(len + 1);
677 mci->mci_sasl_string = xalloc(len + 1);
679 memcpy(mci->mci_sasl_string, out, len);
680 mci->mci_sasl_string[len] = '\0';
681 mci->mci_sasl_string_len = len;
682 # endif /* SASL >= 20000 */
686 ** READAUTH -- read auth values from a file
689 ** filename -- name of file to read.
690 ** safe -- if set, this is a safe read.
691 ** sai -- where to store auth_info.
692 ** rpool -- resource pool for sai.
695 ** EX_OK -- data succesfully read.
696 ** EX_UNAVAILABLE -- no valid filename.
697 ** EX_TEMPFAIL -- temporary failure.
700 static char *sasl_info_name[] =
709 readauth(filename, safe, sai, rpool)
722 if (filename == NULL || filename[0] == '\0')
723 return EX_UNAVAILABLE;
725 #if !_FFR_ALLOW_SASLINFO
727 ** make sure we don't use a program that is not
728 ** accesible to the user who specified a different authinfo file.
729 ** However, currently we don't pass this info (authinfo file
730 ** specified by user) around, so we just turn off program access.
733 if (filename[0] == '|')
738 char *argv[MAXPV + 1];
741 for (p = strtok(&filename[1], " \t"); p != NULL;
742 p = strtok(NULL, " \t"))
749 pid = prog_open(argv, &fd, CurEnv);
753 f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
754 (void *) &fd, SM_IO_RDONLY, NULL);
757 #endif /* !_FFR_ALLOW_SASLINFO */
760 sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
761 |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
762 # if _FFR_GROUPREADABLEAUTHINFOFILE
763 if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
764 # endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
765 sff |= SFF_NOGRFILES;
766 if (DontLockReadFiles)
769 #if _FFR_ALLOW_SASLINFO
771 ** XXX: make sure we don't read or open files that are not
772 ** accesible to the user who specified a different authinfo
777 #else /* _FFR_ALLOW_SASLINFO */
779 sff |= SFF_OPENASROOT;
780 #endif /* _FFR_ALLOW_SASLINFO */
782 f = safefopen(filename, O_RDONLY, 0, sff);
787 sm_syslog(LOG_ERR, NOQID,
788 "AUTH=client, error: can't open %s: %s",
789 filename, sm_errstring(errno));
794 while (lc <= SASL_MECHLIST &&
795 sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
799 (*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
800 if ((s = strchr((*sai)[lc], '\n')) != NULL)
806 (void) sm_io_close(f, SM_TIME_DEFAULT);
809 if (lc < SASL_PASSWORD)
812 sm_syslog(LOG_ERR, NOQID,
813 "AUTH=client, error: can't read %s from %s",
814 sasl_info_name[lc + 1], filename);
821 ** GETAUTH -- get authinfo from ruleset call
823 ** {server_name}, {server_addr} must be set
826 ** mci -- the mailer connection structure.
827 ** e -- the envelope (including the sender to specify).
828 ** sai -- pointer to authinfo (result).
831 ** EX_OK -- ruleset was succesfully called, data may not
832 ** be available, sai must be checked.
833 ** EX_UNAVAILABLE -- ruleset unavailable (or failed).
834 ** EX_TEMPFAIL -- temporary failure (from ruleset).
837 ** Fills in sai if successful.
846 int i, r, l, got, ret;
848 char pvpbuf[PSBUFSIZE];
850 r = rscap("authinfo", macvalue(macid("{server_name}"), e),
851 macvalue(macid("{server_addr}"), e), e,
852 &pvp, pvpbuf, sizeof(pvpbuf));
855 return EX_UNAVAILABLE;
857 /* other than expected return value: ok (i.e., no auth) */
858 if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
860 if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
864 ** parse the data, put it into sai
865 ** format: "TDstring" (including the '"' !)
866 ** where T is a tag: 'U', ...
867 ** D is a delimiter: ':' or '='
870 ret = EX_OK; /* default return value */
873 while (i < SASL_ENTRIES)
875 if (pvp[i + 1] == NULL)
877 if (pvp[i + 1][0] != '"')
879 switch (pvp[i + 1][1])
904 l = strlen(pvp[i + 1]);
907 if (l <= 3 || pvp[i + 1][l - 1] != '"')
910 /* remove closing quote */
911 pvp[i + 1][l - 1] = '\0';
913 /* remove "TD and " */
915 (*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
916 if ((*sai)[r] == NULL)
918 if (pvp[i + 1][2] == ':')
920 /* ':text' (just copy) */
921 (void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
924 else if (pvp[i + 1][2] == '=')
928 /* '=base64' (decode) */
930 ret = sasl_decode64(pvp[i + 1] + 3,
931 (unsigned int) l, (*sai)[r],
932 (unsigned int) l + 1, &len);
933 # else /* SASL >= 20000 */
934 ret = sasl_decode64(pvp[i + 1] + 3,
935 (unsigned int) l, (*sai)[r], &len);
936 # endif /* SASL >= 20000 */
944 sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
945 sasl_info_name[r], (*sai)[r]);
949 /* did we get the expected data? */
950 /* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
951 if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
952 bitset(SASL_PASSWORD_BIT, got)))
955 /* no authid? copy uid */
956 if (!bitset(SASL_AUTHID_BIT, got))
958 l = strlen((*sai)[SASL_USER]) + 1;
959 (*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
961 if ((*sai)[SASL_AUTHID] == NULL)
963 (void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
966 /* no uid? copy authid */
967 if (!bitset(SASL_USER_BIT, got))
969 l = strlen((*sai)[SASL_AUTHID]) + 1;
970 (*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
972 if ((*sai)[SASL_USER] == NULL)
974 (void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
982 sm_syslog(LOG_WARNING, NOQID,
983 "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
984 macvalue(macid("{server_name}"), e),
985 macvalue(macid("{server_addr}"), e),
986 ret == EX_TEMPFAIL ? "temp" : "");
987 for (i = 0; i <= SASL_MECHLIST; i++)
988 (*sai)[i] = NULL; /* just clear; rpool */
994 ** GETSIMPLE -- callback to get userid or authid
999 ** result -- (pointer to) result
1000 ** len -- (pointer to) length of result
1003 ** OK/failure values
1007 getsimple(context, id, result, len)
1010 const char **result;
1015 if (result == NULL || context == NULL)
1016 return SASL_BADPARAM;
1017 sai = (SASL_AI_T *) context;
1022 *result = (*sai)[SASL_USER];
1024 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1027 *len = *result != NULL ? strlen(*result) : 0;
1030 case SASL_CB_AUTHNAME:
1031 *result = (*sai)[SASL_AUTHID];
1033 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1036 *len = *result != NULL ? strlen(*result) : 0;
1039 case SASL_CB_LANGUAGE:
1046 return SASL_BADPARAM;
1051 ** GETSECRET -- callback to get password
1054 ** conn -- connection information
1057 ** psecret -- (pointer to) result
1060 ** OK/failure values
1064 getsecret(conn, context, id, psecret)
1066 SM_UNUSED(void *context);
1068 sasl_secret_t **psecret;
1074 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1075 return SASL_BADPARAM;
1077 mci = (MCI *) context;
1078 authpass = mci->mci_sai[SASL_PASSWORD];
1079 len = strlen(authpass);
1082 ** use an rpool because we are responsible for free()ing the secret,
1083 ** but we can't free() it until after the auth completes
1086 *psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1087 sizeof(sasl_secret_t) +
1089 if (*psecret == NULL)
1091 (void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1092 (*psecret)->len = (unsigned long) len;
1095 # else /* SASL >= 20000 */
1097 ** GETSIMPLE -- callback to get userid or authid
1102 ** result -- (pointer to) result
1103 ** len -- (pointer to) length of result
1106 ** OK/failure values
1110 getsimple(context, id, result, len)
1113 const char **result;
1119 # endif /* SASL > 10509 */
1122 char *authid = NULL;
1124 if (result == NULL || context == NULL)
1125 return SASL_BADPARAM;
1126 sai = (SASL_AI_T *) context;
1129 ** Unfortunately it is not clear whether this routine should
1130 ** return a copy of a string or just a pointer to a string.
1131 ** The Cyrus-SASL plugins treat these return values differently, e.g.,
1132 ** plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1133 ** The best solution to this problem is to fix Cyrus-SASL, but it
1134 ** seems there is nobody who creates patches... Hello CMU!?
1135 ** The second best solution is to have flags that tell this routine
1136 ** whether to return an malloc()ed copy.
1137 ** The next best solution is to always return an malloc()ed copy,
1138 ** and suffer from some memory leak, which is ugly for persistent
1140 ** For now we go with the last solution...
1141 ** We can't use rpools (which would avoid this particular problem)
1142 ** as explained in sasl.c.
1148 l = strlen((*sai)[SASL_USER]) + 1;
1149 s = sm_sasl_malloc(l);
1157 (void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1160 sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1163 *len = *result != NULL ? strlen(*result) : 0;
1166 case SASL_CB_AUTHNAME:
1167 h = (*sai)[SASL_AUTHID];
1169 /* XXX maybe other mechanisms too?! */
1170 addrealm = (*sai)[SASL_MECH] != NULL &&
1171 sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
1174 ** Add realm to authentication id unless authid contains
1175 ** '@' (i.e., a realm) or the default realm is empty.
1178 if (addrealm && h != NULL && strchr(h, '@') == NULL)
1180 /* has this been done before? */
1181 if ((*sai)[SASL_ID_REALM] == NULL)
1185 realm = (*sai)[SASL_DEFREALM];
1187 /* do not add an empty realm */
1191 (*sai)[SASL_ID_REALM] = NULL;
1195 l = strlen(h) + strlen(realm) + 2;
1197 /* should use rpool, but from where? */
1198 authid = sm_sasl_malloc(l);
1201 (void) sm_snprintf(authid, l,
1204 (*sai)[SASL_ID_REALM] = authid;
1209 (*sai)[SASL_ID_REALM] = NULL;
1214 authid = (*sai)[SASL_ID_REALM];
1217 # endif /* SASL > 10509 */
1219 l = strlen(authid) + 1;
1220 s = sm_sasl_malloc(l);
1228 (void) sm_strlcpy(s, authid, l);
1231 sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1234 *len = authid ? strlen(authid) : 0;
1237 case SASL_CB_LANGUAGE:
1244 return SASL_BADPARAM;
1249 ** GETSECRET -- callback to get password
1252 ** conn -- connection information
1255 ** psecret -- (pointer to) result
1258 ** OK/failure values
1262 getsecret(conn, context, id, psecret)
1264 SM_UNUSED(void *context);
1266 sasl_secret_t **psecret;
1272 if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1273 return SASL_BADPARAM;
1275 sai = (SASL_AI_T *) context;
1276 authpass = (*sai)[SASL_PASSWORD];
1277 len = strlen(authpass);
1278 *psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1280 if (*psecret == NULL)
1282 (void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1283 (*psecret)->len = (unsigned long) len;
1286 # endif /* SASL >= 20000 */
1289 ** SAFESASLFILE -- callback for sasl: is file safe?
1292 ** context -- pointer to context between invocations (unused)
1293 ** file -- name of file to check
1294 ** type -- type of file to check
1297 ** SASL_OK -- file can be used
1298 ** SASL_CONTINUE -- don't use file
1299 ** SASL_FAIL -- failure (not used here)
1305 safesaslfile(context, file, type)
1306 #else /* SASL > 10515 */
1307 safesaslfile(context, file)
1308 #endif /* SASL > 10515 */
1312 # else /* SASL >= 20000 */
1314 # endif /* SASL >= 20000 */
1317 sasl_verify_type_t type;
1318 # else /* SASL >= 20000 */
1320 # endif /* SASL >= 20000 */
1321 #endif /* SASL > 10515 */
1327 #endif /* SASL <= 10515 */
1330 if (file == NULL || *file == '\0')
1332 sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1334 if ((p = strrchr(file, '/')) == NULL)
1339 /* everything beside libs and .conf files must not be readable */
1341 if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1342 (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1344 if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1345 sff |= SFF_NORFILES;
1346 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1347 sff |= SFF_NOGWFILES;
1349 #else /* SASL <= 10515 */
1350 /* files containing passwords should be not readable */
1351 if (type == SASL_VRFY_PASSWD)
1353 if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1354 sff |= SFF_NOWRFILES;
1356 sff |= SFF_NORFILES;
1357 if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1358 sff |= SFF_NOGWFILES;
1360 #endif /* SASL <= 10515 */
1363 if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1364 S_IRUSR, NULL)) == 0)
1366 if (LogLevel > (r != ENOENT ? 8 : 10))
1367 sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1368 p, sm_errstring(r));
1369 return SASL_CONTINUE;
1373 ** SASLGETREALM -- return the realm for SASL
1375 ** return the realm for the client
1378 ** context -- context shared between invocations
1379 ** availrealms -- list of available realms
1380 ** {realm, realm, ...}
1381 ** result -- pointer to result
1388 saslgetrealm(context, id, availrealms, result)
1391 const char **availrealms;
1392 const char **result;
1397 sai = (SASL_AI_T *) context;
1400 r = (*sai)[SASL_DEFREALM];
1403 sm_syslog(LOG_INFO, NOQID,
1404 "AUTH=client, realm=%s, available realms=%s",
1405 r == NULL ? "<No Realm>" : r,
1406 (availrealms == NULL || *availrealms == NULL)
1407 ? "<No Realms>" : *availrealms);
1409 /* check whether context is in list */
1410 if (availrealms != NULL && *availrealms != NULL)
1412 if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1416 sm_syslog(LOG_ERR, NOQID,
1417 "AUTH=client, realm=%s not in list=%s",
1426 ** ITEMINLIST -- does item appear in list?
1428 ** Check whether item appears in list (which must be separated by a
1429 ** character in delim) as a "word", i.e. it must appear at the begin
1430 ** of the list or after a space, and it must end with a space or the
1434 ** item -- item to search.
1435 ** list -- list of items.
1436 ** delim -- list of delimiters.
1439 ** pointer to occurrence (NULL if not found).
1443 iteminlist(item, list, delim)
1451 if (list == NULL || *list == '\0')
1453 if (item == NULL || *item == '\0')
1457 while (s != NULL && *s != '\0')
1459 if (sm_strncasecmp(s, item, len) == 0 &&
1460 (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1462 s = strpbrk(s, delim);
1470 ** REMOVEMECH -- remove item [rem] from list [list]
1473 ** rem -- item to remove
1474 ** list -- list of items
1475 ** rpool -- resource pool from which result is allocated.
1478 ** pointer to new list (NULL in case of error).
1482 removemech(rem, list, rpool)
1493 if (rem == NULL || *rem == '\0')
1495 /* take out what? */
1499 /* find the item in the list */
1500 if ((needle = iteminlist(rem, list, " ")) == NULL)
1502 /* not in there: return original */
1506 /* length of string without rem */
1507 len = strlen(list) - strlen(rem);
1510 ret = (char *) sm_rpool_malloc_x(rpool, 1);
1514 ret = (char *) sm_rpool_malloc_x(rpool, len);
1515 memset(ret, '\0', len);
1517 /* copy from start to removed item */
1518 memcpy(ret, list, needle - list);
1520 /* length of rest of string past removed item */
1521 len = strlen(needle) - strlen(rem) - 1;
1524 /* not last item -- copy into string */
1525 memcpy(ret + (needle - list),
1526 list + (needle - list) + strlen(rem) + 1,
1530 ret[(needle - list) - 1] = '\0';
1534 ** ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1538 ** mci -- the mailer connection structure.
1539 ** e -- the envelope (including the sender to specify).
1540 ** sai - sasl authinfo
1543 ** EX_OK -- authentication was successful.
1544 ** EX_NOPERM -- authentication failed.
1545 ** EX_IOERR -- authentication dialogue failed (I/O problem?).
1546 ** EX_TEMPFAIL -- temporary failure.
1551 attemptauth(m, mci, e, sai)
1557 int saslresult, smtpresult;
1560 const char *auth_id;
1562 # else /* SASL >= 20000 */
1563 sasl_external_properties_t ssf;
1565 # endif /* SASL >= 20000 */
1566 unsigned int outlen;
1567 sasl_interact_t *client_interact = NULL;
1569 sasl_security_properties_t ssp;
1570 char in64[MAXOUTLEN];
1571 #if NETINET || (NETINET6 && SASL >= 20000)
1572 extern SOCKADDR CurHostAddr;
1573 #endif /* NETINET || (NETINET6 && SASL >= 20000) */
1575 /* no mechanism selected (yet) */
1576 (*sai)[SASL_MECH] = NULL;
1578 /* dispose old connection */
1579 if (mci->mci_conn != NULL)
1580 sasl_dispose(&(mci->mci_conn));
1582 /* make a new client sasl connection */
1584 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1586 CurHostName, NULL, NULL, NULL, 0,
1588 # else /* SASL >= 20000 */
1589 saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1591 CurHostName, NULL, 0, &mci->mci_conn);
1592 # endif /* SASL >= 20000 */
1593 if (saslresult != SASL_OK)
1596 /* set properties */
1597 (void) memset(&ssp, '\0', sizeof ssp);
1599 /* XXX should these be options settable via .cf ? */
1601 #endif /* STARTTLS */
1603 ssp.max_ssf = MaxSLBits;
1604 ssp.maxbufsize = MAXOUTLEN;
1606 ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1609 saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1610 if (saslresult != SASL_OK)
1614 /* external security strength factor, authentication id */
1618 out = macvalue(macid("{cert_subject}"), e);
1619 if (out != NULL && *out != '\0')
1621 out = macvalue(macid("{cipher_bits}"), e);
1622 if (out != NULL && *out != '\0')
1624 # endif /* STARTTLS */
1625 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1626 if (saslresult != SASL_OK)
1628 saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1629 if (saslresult != SASL_OK)
1632 # if NETINET || NETINET6
1633 /* set local/remote ipv4 addresses */
1634 if (mci->mci_out != NULL && (
1636 CurHostAddr.sa.sa_family == AF_INET6 ||
1637 # endif /* NETINET6 */
1638 CurHostAddr.sa.sa_family == AF_INET))
1640 SOCKADDR_LEN_T addrsize;
1642 char localip[60], remoteip[60];
1644 switch (CurHostAddr.sa.sa_family)
1647 addrsize = sizeof(struct sockaddr_in);
1651 addrsize = sizeof(struct sockaddr_in6);
1653 # endif /* NETINET6 */
1657 if (iptostring(&CurHostAddr, addrsize,
1658 remoteip, sizeof remoteip))
1660 if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1661 remoteip) != SASL_OK)
1664 addrsize = sizeof(saddr_l);
1665 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1667 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1669 if (iptostring(&saddr_l, addrsize,
1670 localip, sizeof localip))
1672 if (sasl_setprop(mci->mci_conn,
1674 localip) != SASL_OK)
1679 # endif /* NETINET || NETINET6 */
1681 /* start client side of sasl */
1682 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1685 (const char **) &mechusing);
1686 # else /* SASL >= 20000 */
1687 /* external security strength factor, authentication id */
1691 out = macvalue(macid("{cert_subject}"), e);
1692 if (out != NULL && *out != '\0')
1694 out = macvalue(macid("{cipher_bits}"), e);
1695 if (out != NULL && *out != '\0')
1696 ssf.ssf = atoi(out);
1697 # endif /* STARTTLS */
1698 saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1699 if (saslresult != SASL_OK)
1703 /* set local/remote ipv4 addresses */
1704 if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1706 SOCKADDR_LEN_T addrsize;
1707 struct sockaddr_in saddr_l;
1709 if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1710 (struct sockaddr_in *) &CurHostAddr)
1713 addrsize = sizeof(struct sockaddr_in);
1714 if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1716 (struct sockaddr *) &saddr_l, &addrsize) == 0)
1718 if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1719 &saddr_l) != SASL_OK)
1723 # endif /* NETINET */
1725 /* start client side of sasl */
1726 saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1727 NULL, &client_interact,
1729 (const char **) &mechusing);
1730 # endif /* SASL >= 20000 */
1732 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1734 if (saslresult == SASL_NOMECH && LogLevel > 8)
1736 sm_syslog(LOG_NOTICE, e->e_id,
1737 "AUTH=client, available mechanisms do not fulfill requirements");
1742 /* just point current mechanism to the data in the sasl library */
1743 (*sai)[SASL_MECH] = mechusing;
1745 /* send the info across the wire */
1747 #if _FFR_SASL_INITIAL_WORKAROUND
1748 /* login and digest-md5 up to 1.5.28 set out="" */
1750 (sm_strcasecmp(mechusing, "LOGIN") == 0 ||
1751 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
1752 #endif /* _FFR_SASL_INITIAL_WORKAROUND */
1755 /* no initial response */
1756 smtpmessage("AUTH %s", m, mci, mechusing);
1758 else if (outlen == 0)
1761 ** zero-length initial response, per RFC 2554 4.:
1762 ** "Unlike a zero-length client answer to a 334 reply, a zero-
1763 ** length initial response is sent as a single equals sign"
1766 smtpmessage("AUTH %s =", m, mci, mechusing);
1770 saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1771 if (saslresult != SASL_OK) /* internal error */
1774 sm_syslog(LOG_ERR, e->e_id,
1775 "encode64 for AUTH failed");
1778 smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1781 sm_sasl_free(out); /* XXX only if no rpool is used */
1782 # endif /* SASL < 20000 */
1785 smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL);
1789 /* check return code from server */
1790 if (smtpresult == 235)
1792 macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1796 if (smtpresult == -1)
1798 if (REPLYTYPE(smtpresult) == 5)
1799 return EX_NOPERM; /* ugly, but ... */
1800 if (REPLYTYPE(smtpresult) != 3)
1802 /* should we fail deliberately, see RFC 2554 4. ? */
1803 /* smtpmessage("*", m, mci); */
1807 saslresult = sasl_client_step(mci->mci_conn,
1808 mci->mci_sasl_string,
1809 mci->mci_sasl_string_len,
1813 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1816 sm_dprintf("AUTH FAIL=%s (%d)\n",
1817 sasl_errstring(saslresult, NULL, NULL),
1820 /* fail deliberately, see RFC 2554 4. */
1821 smtpmessage("*", m, mci);
1824 ** but we should only fail for this authentication
1825 ** mechanism; how to do that?
1828 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1835 saslresult = sasl_encode64(out, outlen, in64,
1837 if (saslresult != SASL_OK)
1839 /* give an error reply to the other side! */
1840 smtpmessage("*", m, mci);
1847 sm_sasl_free(out); /* XXX only if no rpool is used */
1848 # endif /* SASL < 20000 */
1849 smtpmessage("%s", m, mci, in64);
1850 smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1856 ** SMTPAUTH -- try to AUTHenticate
1858 ** This will try mechanisms in the order the sasl library decided until:
1859 ** - there are no more mechanisms
1860 ** - a mechanism succeeds
1861 ** - the sasl library fails initializing
1865 ** mci -- the mailer connection info.
1866 ** e -- the envelope.
1869 ** EX_OK -- authentication was successful
1870 ** EX_UNAVAILABLE -- authentication not possible, e.g.,
1871 ** no data available.
1872 ** EX_NOPERM -- authentication failed.
1873 ** EX_TEMPFAIL -- temporary failure.
1875 ** Notice: AuthInfo is used for all connections, hence we must
1876 ** return EX_TEMPFAIL only if we really want to retry, i.e.,
1877 ** iff getauth() tempfailed or getauth() was used and
1878 ** authentication tempfailed.
1891 mci->mci_sasl_auth = false;
1892 for (i = 0; i < SASL_MECH ; i++)
1893 mci->mci_sai[i] = NULL;
1895 result = getauth(mci, e, &(mci->mci_sai));
1896 if (result == EX_TEMPFAIL)
1900 /* no data available: don't try to authenticate */
1901 if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1903 if (result != EX_OK)
1905 if (SASLInfo == NULL)
1906 return EX_UNAVAILABLE;
1908 /* read authinfo from file */
1909 result = readauth(SASLInfo, true, &(mci->mci_sai),
1911 if (result != EX_OK)
1913 usedgetauth = false;
1916 /* check whether sufficient data is available */
1917 if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1918 *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1919 return EX_UNAVAILABLE;
1920 if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1921 *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1922 (mci->mci_sai[SASL_USER] == NULL ||
1923 *(mci->mci_sai)[SASL_USER] == '\0'))
1924 return EX_UNAVAILABLE;
1926 /* set the context for the callback function to sai */
1928 callbacks[CB_PASS_IDX].context = (void *) mci;
1929 # else /* SASL >= 20000 */
1930 callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1931 # endif /* SASL >= 20000 */
1932 callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1933 callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1934 callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1936 callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
1939 /* set default value for realm */
1940 if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
1941 (mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
1942 macvalue('j', CurEnv));
1944 /* set default value for list of mechanism to use */
1945 if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
1946 *(mci->mci_sai)[SASL_MECHLIST] == '\0')
1947 (mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
1949 /* create list of mechanisms to try */
1950 mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
1951 mci->mci_saslcap, mci->mci_rpool);
1953 /* initialize sasl client library */
1954 result = init_sasl_client();
1955 if (result != SASL_OK)
1956 return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
1959 result = attemptauth(m, mci, e, &(mci->mci_sai));
1960 if (result == EX_OK)
1961 mci->mci_sasl_auth = true;
1962 else if (result == EX_TEMPFAIL || result == EX_NOPERM)
1964 mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
1967 if (mci->mci_saslcap == NULL ||
1968 *(mci->mci_saslcap) == '\0')
1969 return usedgetauth ? result
1974 } while (result != EX_OK);
1980 ** SMTPMAILFROM -- send MAIL command
1984 ** mci -- the mailer connection structure.
1985 ** e -- the envelope (including the sender to specify).
1989 smtpmailfrom(m, mci, e)
1998 char buf[MAXNAME + 1];
1999 char optbuf[MAXLINE];
2002 sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
2006 ** Check if connection is gone, if so
2007 ** it's a tempfail and we use mci_errno
2011 if (mci->mci_state == MCIS_CLOSED)
2013 errno = mci->mci_errno;
2017 /* set up appropriate options to include */
2018 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2020 (void) sm_snprintf(optbuf, sizeof optbuf, " SIZE=%ld",
2022 bufp = &optbuf[strlen(optbuf)];
2030 bodytype = e->e_bodytype;
2031 if (bitset(MCIF_8BITMIME, mci->mci_flags))
2033 if (bodytype == NULL &&
2034 bitset(MM_MIME8BIT, MimeMode) &&
2035 bitset(EF_HAS8BIT, e->e_flags) &&
2036 !bitset(EF_DONT_MIME, e->e_flags) &&
2037 !bitnset(M_8BITS, m->m_flags))
2038 bodytype = "8BITMIME";
2039 if (bodytype != NULL &&
2040 SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2042 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2043 " BODY=%s", bodytype);
2044 bufp += strlen(bufp);
2047 else if (bitnset(M_8BITS, m->m_flags) ||
2048 !bitset(EF_HAS8BIT, e->e_flags) ||
2049 bitset(MCIF_8BITOK, mci->mci_flags))
2052 /* just pass it through */
2055 else if (bitset(MM_CVTMIME, MimeMode) &&
2056 !bitset(EF_DONT_MIME, e->e_flags) &&
2057 (!bitset(MM_PASS8BIT, MimeMode) ||
2058 bitset(EF_IS_MIME, e->e_flags)))
2060 /* must convert from 8bit MIME format to 7bit encoded */
2061 mci->mci_flags |= MCIF_CVT8TO7;
2063 #endif /* MIME8TO7 */
2064 else if (!bitset(MM_PASS8BIT, MimeMode))
2066 /* cannot just send a 8-bit version */
2067 extern char MsgBuf[];
2069 usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2070 mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2074 if (bitset(MCIF_DSN, mci->mci_flags))
2076 if (e->e_envid != NULL &&
2077 SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2079 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2080 " ENVID=%s", e->e_envid);
2081 bufp += strlen(bufp);
2084 /* RET= parameter */
2085 if (bitset(EF_RET_PARAM, e->e_flags) &&
2086 SPACELEFT(optbuf, bufp) > 9)
2088 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2090 bitset(EF_NO_BODY_RETN, e->e_flags) ?
2092 bufp += strlen(bufp);
2096 if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2097 SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2099 && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2103 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2104 " AUTH=%s", e->e_auth_param);
2105 bufp += strlen(bufp);
2109 ** 17 is the max length required, we could use log() to compute
2110 ** the exact length (and check IS_DLVR_TRACE())
2113 if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2114 IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2119 ** Avoid problems with delays (for R) since the check
2120 ** in deliver() whether min-deliver-time is sufficient.
2121 ** Alternatively we could pass the computed time to this
2125 dby = e->e_deliver_by - (curtime() - e->e_ctime);
2126 if (dby <= 0 && IS_DLVR_RETURN(e))
2127 dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2128 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2131 IS_DLVR_RETURN(e) ? 'R' : 'N',
2132 IS_DLVR_TRACE(e) ? "T" : "");
2133 bufp += strlen(bufp);
2137 ** Send the MAIL command.
2138 ** Designates the sender.
2141 mci->mci_state = MCIS_MAIL;
2143 if (bitset(EF_RESPONSE, e->e_flags) &&
2144 !bitnset(M_NO_NULL_FROM, m->m_flags))
2147 expand("\201g", buf, sizeof buf, e);
2150 /* strip off <angle brackets> (put back on below) */
2151 bufp = &buf[strlen(buf) - 1];
2158 if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2159 !bitnset(M_FROMPATH, m->m_flags))
2161 smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2165 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2166 *bufp == '@' ? ',' : ':', bufp, optbuf);
2168 SmtpPhase = mci->mci_phase = "client MAIL";
2169 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2170 CurHostName, mci->mci_phase);
2171 r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
2174 /* communications failure */
2175 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2178 else if (r == SMTPCLOSING)
2180 /* service shutting down: handled by reply() */
2183 else if (REPLYTYPE(r) == 4)
2185 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2189 else if (REPLYTYPE(r) == 2)
2195 /* syntax error in arguments */
2196 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2202 /* mailbox name not allowed */
2203 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2209 /* exceeded storage allocation */
2210 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2212 if (bitset(MCIF_SIZE, mci->mci_flags))
2213 e->e_flags |= EF_NO_BODY_RETN;
2214 return EX_UNAVAILABLE;
2216 else if (REPLYTYPE(r) == 5)
2219 mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2221 return EX_UNAVAILABLE;
2226 sm_syslog(LOG_CRIT, e->e_id,
2227 "%.100s: SMTP MAIL protocol error: %s",
2229 shortenstring(SmtpReplyBuffer, 403));
2232 /* protocol error -- close up */
2233 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2235 smtpquit(m, mci, e);
2239 ** SMTPRCPT -- designate recipient.
2242 ** to -- address of recipient.
2243 ** m -- the mailer we are sending to.
2244 ** mci -- the connection info for this transaction.
2245 ** e -- the envelope for this transaction.
2248 ** exit status corresponding to recipient status.
2251 ** Sends the mail via SMTP.
2255 smtprcpt(to, m, mci, e, ctladdr, xstart)
2264 char optbuf[MAXLINE];
2268 ** If there is status waiting from the other end, read it.
2269 ** This should normally happen because of SMTP pipelining.
2272 while (mci->mci_nextaddr != NULL &&
2273 sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2277 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2280 markfailure(e, mci->mci_nextaddr, mci, r, false);
2281 giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
2282 ctladdr, xstart, e, to);
2284 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2286 #endif /* PIPELINING */
2289 ** Check if connection is gone, if so
2290 ** it's a tempfail and we use mci_errno
2294 if (mci->mci_state == MCIS_CLOSED)
2296 errno = mci->mci_errno;
2304 ** Warning: in the following it is assumed that the free space
2305 ** in bufp is sizeof optbuf
2308 if (bitset(MCIF_DSN, mci->mci_flags))
2310 if (IS_DLVR_NOTIFY(e) &&
2311 !bitset(MCIF_DLVR_BY, mci->mci_flags))
2313 /* RFC 2852: 4.1.4.2 */
2314 if (!bitset(QHASNOTIFY, to->q_flags))
2315 to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2316 else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2317 bitset(QPINGONFAILURE, to->q_flags) ||
2318 bitset(QPINGONDELAY, to->q_flags))
2319 to->q_flags |= QPINGONDELAY;
2322 /* NOTIFY= parameter */
2323 if (bitset(QHASNOTIFY, to->q_flags) &&
2324 bitset(QPRIMARY, to->q_flags) &&
2325 !bitnset(M_LOCALMAILER, m->m_flags))
2327 bool firstone = true;
2329 (void) sm_strlcat(bufp, " NOTIFY=", sizeof optbuf);
2330 if (bitset(QPINGONSUCCESS, to->q_flags))
2332 (void) sm_strlcat(bufp, "SUCCESS", sizeof optbuf);
2335 if (bitset(QPINGONFAILURE, to->q_flags))
2338 (void) sm_strlcat(bufp, ",",
2340 (void) sm_strlcat(bufp, "FAILURE", sizeof optbuf);
2343 if (bitset(QPINGONDELAY, to->q_flags))
2346 (void) sm_strlcat(bufp, ",",
2348 (void) sm_strlcat(bufp, "DELAY", sizeof optbuf);
2352 (void) sm_strlcat(bufp, "NEVER", sizeof optbuf);
2353 bufp += strlen(bufp);
2356 /* ORCPT= parameter */
2357 if (to->q_orcpt != NULL &&
2358 SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2360 (void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2361 " ORCPT=%s", to->q_orcpt);
2362 bufp += strlen(bufp);
2366 smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
2367 mci->mci_state = MCIS_RCPT;
2369 SmtpPhase = mci->mci_phase = "client RCPT";
2370 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2371 CurHostName, mci->mci_phase);
2375 ** If running SMTP pipelining, we will pick up status later
2378 if (bitset(MCIF_PIPELINED, mci->mci_flags))
2380 #endif /* PIPELINING */
2382 return smtprcptstat(to, m, mci, e);
2385 ** SMTPRCPTSTAT -- get recipient status
2387 ** This is only called during SMTP pipelining
2390 ** to -- address of recipient.
2391 ** m -- mailer being sent to.
2392 ** mci -- the mailer connection information.
2393 ** e -- the envelope for this message.
2396 ** EX_* -- protocol status
2400 smtprcptstat(to, m, mci, e)
2404 register ENVELOPE *e;
2411 ** Check if connection is gone, if so
2412 ** it's a tempfail and we use mci_errno
2416 if (mci->mci_state == MCIS_CLOSED)
2418 errno = mci->mci_errno;
2423 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
2425 to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
2426 to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2427 if (!bitnset(M_LMTP, m->m_flags))
2428 to->q_statmta = mci->mci_host;
2429 if (r < 0 || REPLYTYPE(r) == 4)
2431 mci->mci_retryrcpt = true;
2435 else if (REPLYTYPE(r) == 2)
2439 if ((t = mci->mci_tolist) != NULL)
2444 for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2447 mci->mci_tolist = t;
2451 #endif /* PIPELINING */
2456 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2461 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2466 to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2469 else if (REPLYTYPE(r) == 5)
2471 return EX_UNAVAILABLE;
2476 sm_syslog(LOG_CRIT, e->e_id,
2477 "%.100s: SMTP RCPT protocol error: %s",
2479 shortenstring(SmtpReplyBuffer, 403));
2482 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2487 ** SMTPDATA -- send the data and clean up the transaction.
2490 ** m -- mailer being sent to.
2491 ** mci -- the mailer connection information.
2492 ** e -- the envelope for this message.
2495 ** exit status corresponding to DATA command.
2498 static jmp_buf CtxDataTimeout;
2499 static SM_EVENT *volatile DataTimeout = NULL;
2502 smtpdata(m, mci, e, ctladdr, xstart)
2505 register ENVELOPE *e;
2516 ** Check if connection is gone, if so
2517 ** it's a tempfail and we use mci_errno
2521 if (mci->mci_state == MCIS_CLOSED)
2523 errno = mci->mci_errno;
2531 ** First send the command and check that it is ok.
2532 ** Then send the data (if there are valid recipients).
2533 ** Follow it up with a dot to terminate.
2534 ** Finally get the results of the transaction.
2537 /* send the command and check ok to proceed */
2538 smtpmessage("DATA", m, mci);
2541 if (mci->mci_nextaddr != NULL)
2543 char *oldto = e->e_to;
2545 /* pick up any pending RCPT responses for SMTP pipelining */
2546 while (mci->mci_nextaddr != NULL)
2550 e->e_to = mci->mci_nextaddr->q_paddr;
2551 r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2554 markfailure(e, mci->mci_nextaddr, mci, r,
2556 giveresponse(r, mci->mci_nextaddr->q_status, m,
2557 mci, ctladdr, xstart, e,
2559 if (r == EX_TEMPFAIL)
2560 mci->mci_nextaddr->q_state = QS_RETRY;
2562 mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2566 #endif /* PIPELINING */
2568 /* now proceed with DATA phase */
2569 SmtpPhase = mci->mci_phase = "client DATA 354";
2570 mci->mci_state = MCIS_DATA;
2571 sm_setproctitle(true, e, "%s %s: %s",
2572 qid_printname(e), CurHostName, mci->mci_phase);
2573 r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
2574 if (r < 0 || REPLYTYPE(r) == 4)
2577 smtpquit(m, mci, e);
2578 errno = mci->mci_errno;
2581 else if (REPLYTYPE(r) == 5)
2583 smtprset(m, mci, e);
2585 if (mci->mci_okrcpts <= 0)
2586 return mci->mci_retryrcpt ? EX_TEMPFAIL
2588 #endif /* PIPELINING */
2589 return EX_UNAVAILABLE;
2591 else if (REPLYTYPE(r) != 3)
2595 sm_syslog(LOG_CRIT, e->e_id,
2596 "%.100s: SMTP DATA-1 protocol error: %s",
2598 shortenstring(SmtpReplyBuffer, 403));
2600 smtprset(m, mci, e);
2601 mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2604 if (mci->mci_okrcpts <= 0)
2605 return mci->mci_retryrcpt ? EX_TEMPFAIL
2607 #endif /* PIPELINING */
2612 if (mci->mci_okrcpts > 0)
2614 #endif /* PIPELINING */
2617 ** Set timeout around data writes. Make it at least large
2618 ** enough for DNS timeouts on all recipients plus some fudge
2619 ** factor. The main thing is that it should not be infinite.
2622 if (setjmp(CtxDataTimeout) != 0)
2624 mci->mci_errno = errno;
2625 mci->mci_state = MCIS_ERROR;
2626 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2629 ** If putbody() couldn't finish due to a timeout,
2630 ** rewind it here in the timeout handler. See
2631 ** comments at the end of putbody() for reasoning.
2634 if (e->e_dfp != NULL)
2635 (void) bfrewind(e->e_dfp);
2637 errno = mci->mci_errno;
2638 syserr("451 4.4.1 timeout writing message to %s", CurHostName);
2639 smtpquit(m, mci, e);
2645 /* simulate a DATA timeout */
2649 timeout = DATA_PROGRESS_TIMEOUT;
2651 DataTimeout = sm_setevent(timeout, datatimeout, 0);
2655 ** Output the actual message.
2658 (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
2662 /* simulate a DATA timeout */
2666 (*e->e_putbody)(mci, e, NULL);
2669 ** Cleanup after sending message.
2672 if (DataTimeout != NULL)
2673 sm_clrevent(DataTimeout);
2677 #endif /* PIPELINING */
2679 #if _FFR_CATCH_BROKEN_MTAS
2680 if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2682 /* terminate the message */
2683 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2685 if (TrafficLogFile != NULL)
2686 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2687 "%05d >>> .\n", (int) CurrentPid);
2691 sm_syslog(LOG_CRIT, e->e_id,
2692 "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2694 mci->mci_errno = EIO;
2695 mci->mci_state = MCIS_ERROR;
2696 mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2697 smtpquit(m, mci, e);
2700 #endif /* _FFR_CATCH_BROKEN_MTAS */
2702 if (sm_io_error(mci->mci_out))
2704 /* error during processing -- don't send the dot */
2705 mci->mci_errno = EIO;
2706 mci->mci_state = MCIS_ERROR;
2707 mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2708 smtpquit(m, mci, e);
2712 /* terminate the message */
2713 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s", m->m_eol);
2714 if (TrafficLogFile != NULL)
2715 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2716 "%05d >>> .\n", (int) CurrentPid);
2720 /* check for the results of the transaction */
2721 SmtpPhase = mci->mci_phase = "client DATA status";
2722 sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2723 CurHostName, mci->mci_phase);
2724 if (bitnset(M_LMTP, m->m_flags))
2726 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2729 mci->mci_state = MCIS_OPEN;
2730 xstat = EX_NOTSTICKY;
2732 rstat = EX_TEMPFAIL;
2733 else if (REPLYTYPE(r) == 4)
2734 rstat = xstat = EX_TEMPFAIL;
2735 else if (REPLYTYPE(r) == 2)
2736 rstat = xstat = EX_OK;
2737 else if (REPLYCLASS(r) != 5)
2738 rstat = xstat = EX_PROTOCOL;
2739 else if (REPLYTYPE(r) == 5)
2740 rstat = EX_UNAVAILABLE;
2742 rstat = EX_PROTOCOL;
2743 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2745 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2746 (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2750 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2751 SmtpPhase = mci->mci_phase = "idle";
2752 sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2753 if (rstat != EX_PROTOCOL)
2757 sm_syslog(LOG_CRIT, e->e_id,
2758 "%.100s: SMTP DATA-2 protocol error: %s",
2760 shortenstring(SmtpReplyBuffer, 403));
2768 int save_errno = errno;
2771 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
2772 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2780 /* check back again later */
2783 /* simulate a DATA timeout */
2787 timeout = DATA_PROGRESS_TIMEOUT;
2789 /* reset the timeout */
2790 DataTimeout = sm_sigsafe_setevent(timeout, datatimeout, 0);
2791 DataProgress = false;
2799 /* if no progress was made or problem resetting event, die now */
2800 if (DataTimeout == NULL)
2803 longjmp(CtxDataTimeout, 1);
2808 ** SMTPGETSTAT -- get status code from DATA in LMTP
2811 ** m -- the mailer to which we are sending the message.
2812 ** mci -- the mailer connection structure.
2813 ** e -- the current envelope.
2816 ** The exit status corresponding to the reply code.
2820 smtpgetstat(m, mci, e)
2832 /* check for the results of the transaction */
2833 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2836 xstat = EX_NOTSTICKY;
2837 if (REPLYTYPE(r) == 4)
2838 status = EX_TEMPFAIL;
2839 else if (REPLYTYPE(r) == 2)
2840 status = xstat = EX_OK;
2841 else if (REPLYCLASS(r) != 5)
2842 status = xstat = EX_PROTOCOL;
2843 else if (REPLYTYPE(r) == 5)
2844 status = EX_UNAVAILABLE;
2846 status = EX_PROTOCOL;
2847 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2848 (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2852 e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2853 mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2854 if (LogLevel > 1 && status == EX_PROTOCOL)
2856 sm_syslog(LOG_CRIT, e->e_id,
2857 "%.100s: SMTP DATA-3 protocol error: %s",
2859 shortenstring(SmtpReplyBuffer, 403));
2864 ** SMTPQUIT -- close the SMTP connection.
2867 ** m -- a pointer to the mailer.
2868 ** mci -- the mailer connection information.
2869 ** e -- the current envelope.
2875 ** sends the final protocol and closes the connection.
2884 bool oldSuprErrs = SuprErrs;
2888 if (mci->mci_state == MCIS_CLOSED)
2891 oldcurhost = CurHostName;
2892 CurHostName = mci->mci_host; /* XXX UGLY XXX */
2893 if (CurHostName == NULL)
2894 CurHostName = MyHostName;
2897 mci->mci_okrcpts = 0;
2898 #endif /* PIPELINING */
2901 ** Suppress errors here -- we may be processing a different
2902 ** job when we do the quit connection, and we don't want the
2903 ** new job to be penalized for something that isn't it's
2909 /* send the quit message if we haven't gotten I/O error */
2910 if (mci->mci_state != MCIS_ERROR &&
2911 mci->mci_state != MCIS_QUITING)
2913 SmtpPhase = "client QUIT";
2914 mci->mci_state = MCIS_QUITING;
2915 smtpmessage("QUIT", m, mci);
2916 (void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2917 SuprErrs = oldSuprErrs;
2918 if (mci->mci_state == MCIS_CLOSED)
2922 /* now actually close the connection and pick up the zombie */
2923 rcode = endmailer(mci, e, NULL);
2926 char *mailer = NULL;
2928 if (mci->mci_mailer != NULL &&
2929 mci->mci_mailer->m_name != NULL)
2930 mailer = mci->mci_mailer->m_name;
2932 /* look for naughty mailers */
2933 sm_syslog(LOG_ERR, e->e_id,
2934 "smtpquit: mailer%s%s exited with exit value %d",
2935 mailer == NULL ? "" : " ",
2936 mailer == NULL ? "" : mailer,
2940 SuprErrs = oldSuprErrs;
2943 CurHostName = oldcurhost;
2947 ** SMTPRSET -- send a RSET (reset) command
2950 ** m -- a pointer to the mailer.
2951 ** mci -- the mailer connection information.
2952 ** e -- the current envelope.
2958 ** closes the connection if there is no reply to RSET.
2969 CurHostName = mci->mci_host; /* XXX UGLY XXX */
2970 if (CurHostName == NULL)
2971 CurHostName = MyHostName;
2974 mci->mci_okrcpts = 0;
2975 #endif /* PIPELINING */
2978 ** Check if connection is gone, if so
2979 ** it's a tempfail and we use mci_errno
2983 if (mci->mci_state == MCIS_CLOSED)
2985 errno = mci->mci_errno;
2989 SmtpPhase = "client RSET";
2990 smtpmessage("RSET", m, mci);
2991 r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2996 ** Any response is deemed to be acceptable.
2997 ** The standard does not state the proper action
2998 ** to take when a value other than 250 is received.
3000 ** However, if 421 is returned for the RSET, leave
3001 ** mci_state as MCIS_SSD (set in reply()).
3004 if (mci->mci_state != MCIS_SSD)
3005 mci->mci_state = MCIS_OPEN;
3008 ** SMTPPROBE -- check the connection state
3011 ** mci -- the mailer connection information.
3017 ** closes the connection if there is no reply to RSET.
3025 MAILER *m = mci->mci_mailer;
3027 extern ENVELOPE BlankEnvelope;
3029 CurHostName = mci->mci_host; /* XXX UGLY XXX */
3030 if (CurHostName == NULL)
3031 CurHostName = MyHostName;
3034 SmtpPhase = "client probe";
3035 smtpmessage("RSET", m, mci);
3036 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
3037 if (REPLYTYPE(r) != 2)
3038 smtpquit(m, mci, e);
3042 ** REPLY -- read arpanet reply
3045 ** m -- the mailer we are reading the reply from.
3046 ** mci -- the mailer connection info structure.
3047 ** e -- the current envelope.
3048 ** timeout -- the timeout for reads.
3049 ** pfunc -- processing function called on each line of response.
3050 ** If null, no special processing is done.
3051 ** enhstat -- optional, returns enhanced error code string (if set)
3054 ** reply code it reads.
3057 ** flushes the mail file.
3061 reply(m, mci, e, timeout, pfunc, enhstat)
3069 register char *bufp;
3071 bool firstline = true;
3072 char junkbuf[MAXLINE];
3073 static char enhstatcode[ENHSCLEN];
3077 ** Flush the output before reading response.
3079 ** For SMTP pipelining, it would be better if we didn't do
3080 ** this if there was already data waiting to be read. But
3081 ** to do it properly means pushing it to the I/O library,
3082 ** since it really needs to be done below the buffer layer.
3085 if (mci->mci_out != NULL)
3086 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3089 sm_dprintf("reply\n");
3092 ** Read the input line, being careful not to hang.
3095 bufp = SmtpReplyBuffer;
3100 /* actually do the read */
3101 if (e->e_xfp != NULL) /* for debugging */
3102 (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3104 /* if we are in the process of closing just give the code */
3105 if (mci->mci_state == MCIS_CLOSED)
3108 /* don't try to read from a non-existant fd */
3109 if (mci->mci_in == NULL)
3111 if (mci->mci_errno == 0)
3112 mci->mci_errno = EBADF;
3114 /* errors on QUIT should be ignored */
3115 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3117 errno = mci->mci_errno;
3120 mci->mci_state = MCIS_ERROR;
3121 smtpquit(m, mci, e);
3122 errno = mci->mci_errno;
3126 if (mci->mci_out != NULL)
3127 (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3129 /* get the line from the other side */
3130 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3132 mci->mci_lastuse = curtime();
3137 extern char MsgBuf[];
3139 /* errors on QUIT should be ignored */
3140 if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3143 /* if the remote end closed early, fake an error */
3147 (void) sm_snprintf(SmtpReplyBuffer,
3148 sizeof SmtpReplyBuffer,
3149 "421 4.4.1 Connection reset by %s",
3153 #else /* ECONNRESET */
3155 #endif /* ECONNRESET */
3158 mci->mci_errno = errno;
3159 oldholderrs = HoldErrs;
3161 usrerr("451 4.4.1 reply: read error from %s",
3163 mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3165 /* if debugging, pause so we can see state */
3168 mci->mci_state = MCIS_ERROR;
3169 smtpquit(m, mci, e);
3175 if (e->e_to != NULL)
3177 (void) sm_snprintf(p,
3180 shortenstring(e->e_to, MAXSHORTSTR));
3183 (void) sm_snprintf(p, SPACELEFT(wbuf, p),
3184 "reply(%.100s) during %s",
3185 CURHOSTNAME, SmtpPhase);
3189 HoldErrs = oldholderrs;
3193 fixcrlf(bufp, true);
3195 /* EHLO failure is not a real error */
3196 if (e->e_xfp != NULL && (bufp[0] == '4' ||
3197 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3199 /* serious error -- log the previous command */
3202 /* inform user who we are chatting with */
3203 (void) sm_io_fprintf(CurEnv->e_xfp,
3205 "... while talking to %s:\n",
3207 SmtpNeedIntro = false;
3209 if (SmtpMsgBuffer[0] != '\0')
3210 (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3211 ">>> %s\n", SmtpMsgBuffer);
3212 SmtpMsgBuffer[0] = '\0';
3214 /* now log the message as from the other side */
3215 (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3219 /* display the input for verbose mode */
3221 nmessage("050 %s", bufp);
3223 /* ignore improperly formatted input */
3224 if (!ISSMTPREPLY(bufp))
3227 if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3229 extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3230 *enhstat = enhstatcode;
3232 /* process the line */
3234 (*pfunc)(bufp, firstline, m, mci, e);
3238 /* decode the reply code */
3241 /* extra semantics: 0xx codes are "informational" */
3245 /* if no continuation lines, return this line */
3249 /* first line of real reply -- ignore rest */
3254 ** Now look at SmtpReplyBuffer -- only care about the first
3255 ** line of the response from here on out.
3258 /* save temporary failure messages for posterity */
3259 if (SmtpReplyBuffer[0] == '4')
3260 (void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof SmtpError);
3262 /* reply code 421 is "Service Shutting Down" */
3263 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3264 mci->mci_state != MCIS_QUITING)
3266 /* send the quit protocol */
3267 mci->mci_state = MCIS_SSD;
3268 smtpquit(m, mci, e);
3274 ** SMTPMESSAGE -- send message to server
3278 ** m -- the mailer to control formatting.
3279 ** a, b, c -- parameters
3285 ** writes message to mci->mci_out.
3291 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3292 #else /* __STDC__ */
3293 smtpmessage(f, m, mci, va_alist)
3298 #endif /* __STDC__ */
3302 SM_VA_START(ap, mci);
3303 (void) sm_vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
3306 if (tTd(18, 1) || Verbose)
3307 nmessage(">>> %s", SmtpMsgBuffer);
3308 if (TrafficLogFile != NULL)
3309 (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3310 "%05d >>> %s\n", (int) CurrentPid,
3312 if (mci->mci_out != NULL)
3314 (void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3315 SmtpMsgBuffer, m == NULL ? "\r\n"
3318 else if (tTd(18, 1))
3320 sm_dprintf("smtpmessage: NULL mci_out\n");