17c478bd9Sstevel@tonic-gate /* 2445f2479Sjbeck * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers. 37c478bd9Sstevel@tonic-gate * All rights reserved. 47c478bd9Sstevel@tonic-gate * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 57c478bd9Sstevel@tonic-gate * Copyright (c) 1988, 1993 67c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 97c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 107c478bd9Sstevel@tonic-gate * the sendmail distribution. 117c478bd9Sstevel@tonic-gate * 127c478bd9Sstevel@tonic-gate */ 137c478bd9Sstevel@tonic-gate 147c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 157c478bd9Sstevel@tonic-gate 167c478bd9Sstevel@tonic-gate #include <sendmail.h> 1749218d4fSjbeck #include <sm/time.h> 187c478bd9Sstevel@tonic-gate 19*058561cbSjbeck SM_RCSID("@(#)$Id: deliver.c,v 8.1010 2006/12/19 01:15:06 ca Exp $") 207c478bd9Sstevel@tonic-gate 217c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT 227c478bd9Sstevel@tonic-gate # include <login_cap.h> 237c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate #if NETINET || NETINET6 267c478bd9Sstevel@tonic-gate # include <arpa/inet.h> 277c478bd9Sstevel@tonic-gate #endif /* NETINET || NETINET6 */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #if STARTTLS || SASL 307c478bd9Sstevel@tonic-gate # include "sfsasl.h" 317c478bd9Sstevel@tonic-gate #endif /* STARTTLS || SASL */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate static int deliver __P((ENVELOPE *, ADDRESS *)); 347c478bd9Sstevel@tonic-gate static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 357c478bd9Sstevel@tonic-gate static void mailfiletimeout __P((int)); 367c478bd9Sstevel@tonic-gate static void endwaittimeout __P((int)); 377c478bd9Sstevel@tonic-gate static int parse_hostsignature __P((char *, char **, MAILER *)); 387c478bd9Sstevel@tonic-gate static void sendenvelope __P((ENVELOPE *, int)); 397c478bd9Sstevel@tonic-gate static int coloncmp __P((const char *, const char *)); 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #if STARTTLS 427c478bd9Sstevel@tonic-gate static int starttls __P((MAILER *, MCI *, ENVELOPE *)); 437c478bd9Sstevel@tonic-gate static int endtlsclt __P((MCI *)); 447c478bd9Sstevel@tonic-gate #endif /* STARTTLS */ 457c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 467c478bd9Sstevel@tonic-gate static bool iscltflgset __P((ENVELOPE *, int)); 477c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate /* 507c478bd9Sstevel@tonic-gate ** SENDALL -- actually send all the messages. 517c478bd9Sstevel@tonic-gate ** 527c478bd9Sstevel@tonic-gate ** Parameters: 537c478bd9Sstevel@tonic-gate ** e -- the envelope to send. 547c478bd9Sstevel@tonic-gate ** mode -- the delivery mode to use. If SM_DEFAULT, use 557c478bd9Sstevel@tonic-gate ** the current e->e_sendmode. 567c478bd9Sstevel@tonic-gate ** 577c478bd9Sstevel@tonic-gate ** Returns: 587c478bd9Sstevel@tonic-gate ** none. 597c478bd9Sstevel@tonic-gate ** 607c478bd9Sstevel@tonic-gate ** Side Effects: 617c478bd9Sstevel@tonic-gate ** Scans the send lists and sends everything it finds. 627c478bd9Sstevel@tonic-gate ** Delivers any appropriate error messages. 637c478bd9Sstevel@tonic-gate ** If we are running in a non-interactive mode, takes the 647c478bd9Sstevel@tonic-gate ** appropriate action. 657c478bd9Sstevel@tonic-gate */ 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate void 687c478bd9Sstevel@tonic-gate sendall(e, mode) 697c478bd9Sstevel@tonic-gate ENVELOPE *e; 707c478bd9Sstevel@tonic-gate int mode; 717c478bd9Sstevel@tonic-gate { 727c478bd9Sstevel@tonic-gate register ADDRESS *q; 737c478bd9Sstevel@tonic-gate char *owner; 747c478bd9Sstevel@tonic-gate int otherowners; 757c478bd9Sstevel@tonic-gate int save_errno; 767c478bd9Sstevel@tonic-gate register ENVELOPE *ee; 777c478bd9Sstevel@tonic-gate ENVELOPE *splitenv = NULL; 787c478bd9Sstevel@tonic-gate int oldverbose = Verbose; 797c478bd9Sstevel@tonic-gate bool somedeliveries = false, expensive = false; 807c478bd9Sstevel@tonic-gate pid_t pid; 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate /* 837c478bd9Sstevel@tonic-gate ** If this message is to be discarded, don't bother sending 847c478bd9Sstevel@tonic-gate ** the message at all. 857c478bd9Sstevel@tonic-gate */ 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate if (bitset(EF_DISCARD, e->e_flags)) 887c478bd9Sstevel@tonic-gate { 897c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 907c478bd9Sstevel@tonic-gate sm_dprintf("sendall: discarding id %s\n", e->e_id); 917c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 927c478bd9Sstevel@tonic-gate if (LogLevel > 9) 937c478bd9Sstevel@tonic-gate logundelrcpts(e, "discarded", 9, true); 947c478bd9Sstevel@tonic-gate else if (LogLevel > 4) 957c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "discarded"); 967c478bd9Sstevel@tonic-gate markstats(e, NULL, STATS_REJECT); 977c478bd9Sstevel@tonic-gate return; 987c478bd9Sstevel@tonic-gate } 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate /* 1017c478bd9Sstevel@tonic-gate ** If we have had global, fatal errors, don't bother sending 1027c478bd9Sstevel@tonic-gate ** the message at all if we are in SMTP mode. Local errors 1037c478bd9Sstevel@tonic-gate ** (e.g., a single address failing) will still cause the other 1047c478bd9Sstevel@tonic-gate ** addresses to be sent. 1057c478bd9Sstevel@tonic-gate */ 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate if (bitset(EF_FATALERRS, e->e_flags) && 1087c478bd9Sstevel@tonic-gate (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 1097c478bd9Sstevel@tonic-gate { 1107c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 1117c478bd9Sstevel@tonic-gate return; 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate /* determine actual delivery mode */ 1157c478bd9Sstevel@tonic-gate if (mode == SM_DEFAULT) 1167c478bd9Sstevel@tonic-gate { 1177c478bd9Sstevel@tonic-gate mode = e->e_sendmode; 1187c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && mode != SM_DEFER && 1197c478bd9Sstevel@tonic-gate shouldqueue(e->e_msgpriority, e->e_ctime)) 1207c478bd9Sstevel@tonic-gate mode = SM_QUEUE; 1217c478bd9Sstevel@tonic-gate } 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 1247c478bd9Sstevel@tonic-gate { 1257c478bd9Sstevel@tonic-gate sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 1267c478bd9Sstevel@tonic-gate mode, e->e_id); 1277c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 1287c478bd9Sstevel@tonic-gate sm_dprintf("\te_flags = "); 1297c478bd9Sstevel@tonic-gate printenvflags(e); 1307c478bd9Sstevel@tonic-gate sm_dprintf("sendqueue:\n"); 1317c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate /* 1357c478bd9Sstevel@tonic-gate ** Do any preprocessing necessary for the mode we are running. 1367c478bd9Sstevel@tonic-gate ** Check to make sure the hop count is reasonable. 1377c478bd9Sstevel@tonic-gate ** Delete sends to the sender in mailing lists. 1387c478bd9Sstevel@tonic-gate */ 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate CurEnv = e; 1417c478bd9Sstevel@tonic-gate if (tTd(62, 1)) 1427c478bd9Sstevel@tonic-gate checkfds(NULL); 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate if (e->e_hopcount > MaxHopCount) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate char *recip; 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate if (e->e_sendqueue != NULL && 1497c478bd9Sstevel@tonic-gate e->e_sendqueue->q_paddr != NULL) 1507c478bd9Sstevel@tonic-gate recip = e->e_sendqueue->q_paddr; 1517c478bd9Sstevel@tonic-gate else 1527c478bd9Sstevel@tonic-gate recip = "(nobody)"; 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate errno = 0; 1557c478bd9Sstevel@tonic-gate queueup(e, WILL_BE_QUEUED(mode), false); 1567c478bd9Sstevel@tonic-gate e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 1577c478bd9Sstevel@tonic-gate ExitStat = EX_UNAVAILABLE; 1587c478bd9Sstevel@tonic-gate syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 1597c478bd9Sstevel@tonic-gate e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 1607c478bd9Sstevel@tonic-gate RealHostName == NULL ? "localhost" : RealHostName, 1617c478bd9Sstevel@tonic-gate recip); 1627c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1637c478bd9Sstevel@tonic-gate { 1647c478bd9Sstevel@tonic-gate if (QS_IS_DEAD(q->q_state)) 1657c478bd9Sstevel@tonic-gate continue; 1667c478bd9Sstevel@tonic-gate q->q_state = QS_BADADDR; 1677c478bd9Sstevel@tonic-gate q->q_status = "5.4.6"; 1687c478bd9Sstevel@tonic-gate q->q_rstatus = "554 5.4.6 Too many hops"; 1697c478bd9Sstevel@tonic-gate } 1707c478bd9Sstevel@tonic-gate return; 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate /* 1747c478bd9Sstevel@tonic-gate ** Do sender deletion. 1757c478bd9Sstevel@tonic-gate ** 1767c478bd9Sstevel@tonic-gate ** If the sender should be queued up, skip this. 1777c478bd9Sstevel@tonic-gate ** This can happen if the name server is hosed when you 1787c478bd9Sstevel@tonic-gate ** are trying to send mail. The result is that the sender 1797c478bd9Sstevel@tonic-gate ** is instantiated in the queue as a recipient. 1807c478bd9Sstevel@tonic-gate */ 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate if (!bitset(EF_METOO, e->e_flags) && 1837c478bd9Sstevel@tonic-gate !QS_IS_QUEUEUP(e->e_from.q_state)) 1847c478bd9Sstevel@tonic-gate { 1857c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 1867c478bd9Sstevel@tonic-gate { 1877c478bd9Sstevel@tonic-gate sm_dprintf("sendall: QS_SENDER "); 1887c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 1897c478bd9Sstevel@tonic-gate } 1907c478bd9Sstevel@tonic-gate e->e_from.q_state = QS_SENDER; 1917c478bd9Sstevel@tonic-gate (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 1927c478bd9Sstevel@tonic-gate } 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate /* 1957c478bd9Sstevel@tonic-gate ** Handle alias owners. 1967c478bd9Sstevel@tonic-gate ** 1977c478bd9Sstevel@tonic-gate ** We scan up the q_alias chain looking for owners. 1987c478bd9Sstevel@tonic-gate ** We discard owners that are the same as the return path. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 2027c478bd9Sstevel@tonic-gate { 2037c478bd9Sstevel@tonic-gate register struct address *a; 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 2067c478bd9Sstevel@tonic-gate continue; 2077c478bd9Sstevel@tonic-gate if (a != NULL) 2087c478bd9Sstevel@tonic-gate q->q_owner = a->q_owner; 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate if (q->q_owner != NULL && 2117c478bd9Sstevel@tonic-gate !QS_IS_DEAD(q->q_state) && 2127c478bd9Sstevel@tonic-gate strcmp(q->q_owner, e->e_from.q_paddr) == 0) 2137c478bd9Sstevel@tonic-gate q->q_owner = NULL; 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate if (tTd(13, 25)) 2177c478bd9Sstevel@tonic-gate { 2187c478bd9Sstevel@tonic-gate sm_dprintf("\nAfter first owner pass, sendq =\n"); 2197c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 2207c478bd9Sstevel@tonic-gate } 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate owner = ""; 2237c478bd9Sstevel@tonic-gate otherowners = 1; 2247c478bd9Sstevel@tonic-gate while (owner != NULL && otherowners > 0) 2257c478bd9Sstevel@tonic-gate { 2267c478bd9Sstevel@tonic-gate if (tTd(13, 28)) 2277c478bd9Sstevel@tonic-gate sm_dprintf("owner = \"%s\", otherowners = %d\n", 2287c478bd9Sstevel@tonic-gate owner, otherowners); 2297c478bd9Sstevel@tonic-gate owner = NULL; 2307c478bd9Sstevel@tonic-gate otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 2337c478bd9Sstevel@tonic-gate { 2347c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate sm_dprintf("Checking "); 2377c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), q, false); 2387c478bd9Sstevel@tonic-gate } 2397c478bd9Sstevel@tonic-gate if (QS_IS_DEAD(q->q_state)) 2407c478bd9Sstevel@tonic-gate { 2417c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 2427c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_DEAD\n"); 2437c478bd9Sstevel@tonic-gate continue; 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate if (tTd(13, 29) && !tTd(13, 30)) 2467c478bd9Sstevel@tonic-gate { 2477c478bd9Sstevel@tonic-gate sm_dprintf("Checking "); 2487c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), q, false); 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate if (q->q_owner != NULL) 2527c478bd9Sstevel@tonic-gate { 2537c478bd9Sstevel@tonic-gate if (owner == NULL) 2547c478bd9Sstevel@tonic-gate { 2557c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 2567c478bd9Sstevel@tonic-gate sm_dprintf(" ... First owner = \"%s\"\n", 2577c478bd9Sstevel@tonic-gate q->q_owner); 2587c478bd9Sstevel@tonic-gate owner = q->q_owner; 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate else if (owner != q->q_owner) 2617c478bd9Sstevel@tonic-gate { 2627c478bd9Sstevel@tonic-gate if (strcmp(owner, q->q_owner) == 0) 2637c478bd9Sstevel@tonic-gate { 2647c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 2657c478bd9Sstevel@tonic-gate sm_dprintf(" ... Same owner = \"%s\"\n", 2667c478bd9Sstevel@tonic-gate owner); 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate /* make future comparisons cheap */ 2697c478bd9Sstevel@tonic-gate q->q_owner = owner; 2707c478bd9Sstevel@tonic-gate } 2717c478bd9Sstevel@tonic-gate else 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 2747c478bd9Sstevel@tonic-gate sm_dprintf(" ... Another owner \"%s\"\n", 2757c478bd9Sstevel@tonic-gate q->q_owner); 2767c478bd9Sstevel@tonic-gate otherowners++; 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate owner = q->q_owner; 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate else if (tTd(13, 40)) 2817c478bd9Sstevel@tonic-gate sm_dprintf(" ... Same owner = \"%s\"\n", 2827c478bd9Sstevel@tonic-gate owner); 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate else 2857c478bd9Sstevel@tonic-gate { 2867c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 2877c478bd9Sstevel@tonic-gate sm_dprintf(" ... Null owner\n"); 2887c478bd9Sstevel@tonic-gate otherowners++; 2897c478bd9Sstevel@tonic-gate } 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate if (QS_IS_BADADDR(q->q_state)) 2927c478bd9Sstevel@tonic-gate { 2937c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 2947c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_BADADDR\n"); 2957c478bd9Sstevel@tonic-gate continue; 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate if (QS_IS_QUEUEUP(q->q_state)) 2997c478bd9Sstevel@tonic-gate { 3007c478bd9Sstevel@tonic-gate MAILER *m = q->q_mailer; 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate /* 3037c478bd9Sstevel@tonic-gate ** If we have temporary address failures 3047c478bd9Sstevel@tonic-gate ** (e.g., dns failure) and a fallback MX is 3057c478bd9Sstevel@tonic-gate ** set, send directly to the fallback MX host. 3067c478bd9Sstevel@tonic-gate */ 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate if (FallbackMX != NULL && 3097c478bd9Sstevel@tonic-gate !wordinclass(FallbackMX, 'w') && 3107c478bd9Sstevel@tonic-gate mode != SM_VERIFY && 3117c478bd9Sstevel@tonic-gate !bitnset(M_NOMX, m->m_flags) && 3127c478bd9Sstevel@tonic-gate strcmp(m->m_mailer, "[IPC]") == 0 && 3137c478bd9Sstevel@tonic-gate m->m_argv[0] != NULL && 3147c478bd9Sstevel@tonic-gate strcmp(m->m_argv[0], "TCP") == 0) 3157c478bd9Sstevel@tonic-gate { 3167c478bd9Sstevel@tonic-gate int len; 3177c478bd9Sstevel@tonic-gate char *p; 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3207c478bd9Sstevel@tonic-gate sm_dprintf(" ... FallbackMX\n"); 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate len = strlen(FallbackMX) + 1; 3237c478bd9Sstevel@tonic-gate p = sm_rpool_malloc_x(e->e_rpool, len); 3247c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, FallbackMX, len); 3257c478bd9Sstevel@tonic-gate q->q_state = QS_OK; 3267c478bd9Sstevel@tonic-gate q->q_host = p; 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate else 3297c478bd9Sstevel@tonic-gate { 3307c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3317c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_QUEUEUP\n"); 3327c478bd9Sstevel@tonic-gate continue; 3337c478bd9Sstevel@tonic-gate } 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate /* 3377c478bd9Sstevel@tonic-gate ** If this mailer is expensive, and if we don't 3387c478bd9Sstevel@tonic-gate ** want to make connections now, just mark these 3397c478bd9Sstevel@tonic-gate ** addresses and return. This is useful if we 3407c478bd9Sstevel@tonic-gate ** want to batch connections to reduce load. This 3417c478bd9Sstevel@tonic-gate ** will cause the messages to be queued up, and a 3427c478bd9Sstevel@tonic-gate ** daemon will come along to send the messages later. 3437c478bd9Sstevel@tonic-gate */ 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate if (NoConnect && !Verbose && 3467c478bd9Sstevel@tonic-gate bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 3477c478bd9Sstevel@tonic-gate { 3487c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3497c478bd9Sstevel@tonic-gate sm_dprintf(" ... expensive\n"); 3507c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 3517c478bd9Sstevel@tonic-gate expensive = true; 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 3547c478bd9Sstevel@tonic-gate QueueLimitId == NULL && 3557c478bd9Sstevel@tonic-gate QueueLimitSender == NULL && 3567c478bd9Sstevel@tonic-gate QueueLimitRecipient == NULL) 3577c478bd9Sstevel@tonic-gate { 3587c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3597c478bd9Sstevel@tonic-gate sm_dprintf(" ... hold\n"); 3607c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 3617c478bd9Sstevel@tonic-gate expensive = true; 3627c478bd9Sstevel@tonic-gate } 3637c478bd9Sstevel@tonic-gate else if (QueueMode != QM_QUARANTINE && 3647c478bd9Sstevel@tonic-gate e->e_quarmsg != NULL) 3657c478bd9Sstevel@tonic-gate { 3667c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3677c478bd9Sstevel@tonic-gate sm_dprintf(" ... quarantine: %s\n", 3687c478bd9Sstevel@tonic-gate e->e_quarmsg); 3697c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 3707c478bd9Sstevel@tonic-gate expensive = true; 3717c478bd9Sstevel@tonic-gate } 3727c478bd9Sstevel@tonic-gate else 3737c478bd9Sstevel@tonic-gate { 3747c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 3757c478bd9Sstevel@tonic-gate sm_dprintf(" ... deliverable\n"); 3767c478bd9Sstevel@tonic-gate somedeliveries = true; 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate if (owner != NULL && otherowners > 0) 3817c478bd9Sstevel@tonic-gate { 3827c478bd9Sstevel@tonic-gate /* 3837c478bd9Sstevel@tonic-gate ** Split this envelope into two. 3847c478bd9Sstevel@tonic-gate */ 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 387*058561cbSjbeck sizeof(*ee)); 3887c478bd9Sstevel@tonic-gate STRUCTCOPY(*e, *ee); 3897c478bd9Sstevel@tonic-gate ee->e_message = NULL; 3907c478bd9Sstevel@tonic-gate ee->e_id = NULL; 3917c478bd9Sstevel@tonic-gate assign_queueid(ee); 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 3947c478bd9Sstevel@tonic-gate sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 3957c478bd9Sstevel@tonic-gate e->e_id, ee->e_id, owner, 3967c478bd9Sstevel@tonic-gate otherowners); 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate ee->e_header = copyheader(e->e_header, ee->e_rpool); 3997c478bd9Sstevel@tonic-gate ee->e_sendqueue = copyqueue(e->e_sendqueue, 4007c478bd9Sstevel@tonic-gate ee->e_rpool); 4017c478bd9Sstevel@tonic-gate ee->e_errorqueue = copyqueue(e->e_errorqueue, 4027c478bd9Sstevel@tonic-gate ee->e_rpool); 4037c478bd9Sstevel@tonic-gate ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 4047c478bd9Sstevel@tonic-gate ee->e_flags |= EF_NORECEIPT; 4057c478bd9Sstevel@tonic-gate setsender(owner, ee, NULL, '\0', true); 4067c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 4077c478bd9Sstevel@tonic-gate { 4087c478bd9Sstevel@tonic-gate sm_dprintf("sendall(split): QS_SENDER "); 4097c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &ee->e_from, false); 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate ee->e_from.q_state = QS_SENDER; 4127c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 4137c478bd9Sstevel@tonic-gate ee->e_lockfp = NULL; 4147c478bd9Sstevel@tonic-gate ee->e_xfp = NULL; 4157c478bd9Sstevel@tonic-gate ee->e_qgrp = e->e_qgrp; 4167c478bd9Sstevel@tonic-gate ee->e_qdir = e->e_qdir; 4177c478bd9Sstevel@tonic-gate ee->e_errormode = EM_MAIL; 4187c478bd9Sstevel@tonic-gate ee->e_sibling = splitenv; 4197c478bd9Sstevel@tonic-gate ee->e_statmsg = NULL; 4207c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 4217c478bd9Sstevel@tonic-gate ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 4227c478bd9Sstevel@tonic-gate e->e_quarmsg); 4237c478bd9Sstevel@tonic-gate splitenv = ee; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 4267c478bd9Sstevel@tonic-gate { 4277c478bd9Sstevel@tonic-gate if (q->q_owner == owner) 4287c478bd9Sstevel@tonic-gate { 4297c478bd9Sstevel@tonic-gate q->q_state = QS_CLONED; 4307c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 4317c478bd9Sstevel@tonic-gate sm_dprintf("\t... stripping %s from original envelope\n", 4327c478bd9Sstevel@tonic-gate q->q_paddr); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 4367c478bd9Sstevel@tonic-gate { 4377c478bd9Sstevel@tonic-gate if (q->q_owner != owner) 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate q->q_state = QS_CLONED; 4407c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 4417c478bd9Sstevel@tonic-gate sm_dprintf("\t... dropping %s from cloned envelope\n", 4427c478bd9Sstevel@tonic-gate q->q_paddr); 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate else 4457c478bd9Sstevel@tonic-gate { 4467c478bd9Sstevel@tonic-gate /* clear DSN parameters */ 4477c478bd9Sstevel@tonic-gate q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 4487c478bd9Sstevel@tonic-gate q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 4497c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 4507c478bd9Sstevel@tonic-gate sm_dprintf("\t... moving %s to cloned envelope\n", 4517c478bd9Sstevel@tonic-gate q->q_paddr); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 4567c478bd9Sstevel@tonic-gate dup_queue_file(e, ee, DATAFL_LETTER); 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate /* 4597c478bd9Sstevel@tonic-gate ** Give the split envelope access to the parent 4607c478bd9Sstevel@tonic-gate ** transcript file for errors obtained while 4617c478bd9Sstevel@tonic-gate ** processing the recipients (done before the 4627c478bd9Sstevel@tonic-gate ** envelope splitting). 4637c478bd9Sstevel@tonic-gate */ 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 4667c478bd9Sstevel@tonic-gate ee->e_xfp = sm_io_dup(e->e_xfp); 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* failed to dup e->e_xfp, start a new transcript */ 4697c478bd9Sstevel@tonic-gate if (ee->e_xfp == NULL) 4707c478bd9Sstevel@tonic-gate openxscript(ee); 4717c478bd9Sstevel@tonic-gate 4727c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && LogLevel > 4) 4737c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 4747c478bd9Sstevel@tonic-gate "%s: clone: owner=%s", 4757c478bd9Sstevel@tonic-gate ee->e_id, owner); 4767c478bd9Sstevel@tonic-gate } 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate if (owner != NULL) 4807c478bd9Sstevel@tonic-gate { 4817c478bd9Sstevel@tonic-gate setsender(owner, e, NULL, '\0', true); 4827c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 4837c478bd9Sstevel@tonic-gate { 4847c478bd9Sstevel@tonic-gate sm_dprintf("sendall(owner): QS_SENDER "); 4857c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate e->e_from.q_state = QS_SENDER; 4887c478bd9Sstevel@tonic-gate e->e_errormode = EM_MAIL; 4897c478bd9Sstevel@tonic-gate e->e_flags |= EF_NORECEIPT; 4907c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_FATALERRS; 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate /* if nothing to be delivered, just queue up everything */ 4947c478bd9Sstevel@tonic-gate if (!somedeliveries && !WILL_BE_QUEUED(mode) && 4957c478bd9Sstevel@tonic-gate mode != SM_VERIFY) 4967c478bd9Sstevel@tonic-gate { 4977c478bd9Sstevel@tonic-gate time_t now; 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate if (tTd(13, 29)) 5007c478bd9Sstevel@tonic-gate sm_dprintf("No deliveries: auto-queuing\n"); 5017c478bd9Sstevel@tonic-gate mode = SM_QUEUE; 5027c478bd9Sstevel@tonic-gate now = curtime(); 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate /* treat this as a delivery in terms of counting tries */ 5057c478bd9Sstevel@tonic-gate e->e_dtime = now; 5067c478bd9Sstevel@tonic-gate if (!expensive) 5077c478bd9Sstevel@tonic-gate e->e_ntries++; 5087c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 5097c478bd9Sstevel@tonic-gate { 5107c478bd9Sstevel@tonic-gate ee->e_dtime = now; 5117c478bd9Sstevel@tonic-gate if (!expensive) 5127c478bd9Sstevel@tonic-gate ee->e_ntries++; 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 5177c478bd9Sstevel@tonic-gate (mode != SM_VERIFY && 5187c478bd9Sstevel@tonic-gate (SuperSafe == SAFE_REALLY || 5197c478bd9Sstevel@tonic-gate SuperSafe == SAFE_REALLY_POSTMILTER))) && 5207c478bd9Sstevel@tonic-gate (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 5217c478bd9Sstevel@tonic-gate { 5227c478bd9Sstevel@tonic-gate bool msync; 5237c478bd9Sstevel@tonic-gate 5247c478bd9Sstevel@tonic-gate /* 5257c478bd9Sstevel@tonic-gate ** Be sure everything is instantiated in the queue. 5267c478bd9Sstevel@tonic-gate ** Split envelopes first in case the machine crashes. 5277c478bd9Sstevel@tonic-gate ** If the original were done first, we may lose 5287c478bd9Sstevel@tonic-gate ** recipients. 5297c478bd9Sstevel@tonic-gate */ 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate #if !HASFLOCK 5327c478bd9Sstevel@tonic-gate msync = false; 5337c478bd9Sstevel@tonic-gate #else /* !HASFLOCK */ 5347c478bd9Sstevel@tonic-gate msync = mode == SM_FORK; 5357c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */ 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 5387c478bd9Sstevel@tonic-gate queueup(ee, WILL_BE_QUEUED(mode), msync); 5397c478bd9Sstevel@tonic-gate queueup(e, WILL_BE_QUEUED(mode), msync); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate if (tTd(62, 10)) 5437c478bd9Sstevel@tonic-gate checkfds("after envelope splitting"); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate /* 5467c478bd9Sstevel@tonic-gate ** If we belong in background, fork now. 5477c478bd9Sstevel@tonic-gate */ 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate if (tTd(13, 20)) 5507c478bd9Sstevel@tonic-gate { 5517c478bd9Sstevel@tonic-gate sm_dprintf("sendall: final mode = %c\n", mode); 5527c478bd9Sstevel@tonic-gate if (tTd(13, 21)) 5537c478bd9Sstevel@tonic-gate { 5547c478bd9Sstevel@tonic-gate sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 5557c478bd9Sstevel@tonic-gate sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 5567c478bd9Sstevel@tonic-gate e->e_id, e->e_from.q_paddr); 5577c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 5587c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 5597c478bd9Sstevel@tonic-gate { 5607c478bd9Sstevel@tonic-gate sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 5617c478bd9Sstevel@tonic-gate ee->e_id, ee->e_from.q_paddr); 5627c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ee->e_sendqueue, true); 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate sm_dprintf("==========================================================\n\n"); 5657c478bd9Sstevel@tonic-gate } 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate switch (mode) 5687c478bd9Sstevel@tonic-gate { 5697c478bd9Sstevel@tonic-gate case SM_VERIFY: 5707c478bd9Sstevel@tonic-gate Verbose = 2; 5717c478bd9Sstevel@tonic-gate break; 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate case SM_QUEUE: 5747c478bd9Sstevel@tonic-gate case SM_DEFER: 5757c478bd9Sstevel@tonic-gate #if HASFLOCK 5767c478bd9Sstevel@tonic-gate queueonly: 5777c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 5787c478bd9Sstevel@tonic-gate if (e->e_nrcpts > 0) 5797c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 5807c478bd9Sstevel@tonic-gate dropenvelope(e, splitenv != NULL, true); 5817c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 5827c478bd9Sstevel@tonic-gate { 5837c478bd9Sstevel@tonic-gate if (ee->e_nrcpts > 0) 5847c478bd9Sstevel@tonic-gate ee->e_flags |= EF_INQUEUE; 5857c478bd9Sstevel@tonic-gate dropenvelope(ee, false, true); 5867c478bd9Sstevel@tonic-gate } 5877c478bd9Sstevel@tonic-gate return; 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate case SM_FORK: 5907c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 5917c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate #if !HASFLOCK 5947c478bd9Sstevel@tonic-gate /* 5957c478bd9Sstevel@tonic-gate ** Since fcntl locking has the interesting semantic that 5967c478bd9Sstevel@tonic-gate ** the lock is owned by a process, not by an open file 5977c478bd9Sstevel@tonic-gate ** descriptor, we have to flush this to the queue, and 5987c478bd9Sstevel@tonic-gate ** then restart from scratch in the child. 5997c478bd9Sstevel@tonic-gate */ 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate { 6027c478bd9Sstevel@tonic-gate /* save id for future use */ 6037c478bd9Sstevel@tonic-gate char *qid = e->e_id; 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate /* now drop the envelope in the parent */ 6067c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 6077c478bd9Sstevel@tonic-gate dropenvelope(e, splitenv != NULL, false); 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate /* arrange to reacquire lock after fork */ 6107c478bd9Sstevel@tonic-gate e->e_id = qid; 6117c478bd9Sstevel@tonic-gate } 6127c478bd9Sstevel@tonic-gate 6137c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 6147c478bd9Sstevel@tonic-gate { 6157c478bd9Sstevel@tonic-gate /* save id for future use */ 6167c478bd9Sstevel@tonic-gate char *qid = ee->e_id; 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate /* drop envelope in parent */ 6197c478bd9Sstevel@tonic-gate ee->e_flags |= EF_INQUEUE; 6207c478bd9Sstevel@tonic-gate dropenvelope(ee, false, false); 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate /* and save qid for reacquisition */ 6237c478bd9Sstevel@tonic-gate ee->e_id = qid; 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */ 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate /* 6297c478bd9Sstevel@tonic-gate ** Since the delivery may happen in a child and the parent 6307c478bd9Sstevel@tonic-gate ** does not wait, the parent may close the maps thereby 6317c478bd9Sstevel@tonic-gate ** removing any shared memory used by the map. Therefore, 6327c478bd9Sstevel@tonic-gate ** close the maps now so the child will dynamically open 6337c478bd9Sstevel@tonic-gate ** them if necessary. 6347c478bd9Sstevel@tonic-gate */ 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate closemaps(false); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate pid = fork(); 6397c478bd9Sstevel@tonic-gate if (pid < 0) 6407c478bd9Sstevel@tonic-gate { 6417c478bd9Sstevel@tonic-gate syserr("deliver: fork 1"); 6427c478bd9Sstevel@tonic-gate #if HASFLOCK 6437c478bd9Sstevel@tonic-gate goto queueonly; 6447c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 6457c478bd9Sstevel@tonic-gate e->e_id = NULL; 6467c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 6477c478bd9Sstevel@tonic-gate ee->e_id = NULL; 6487c478bd9Sstevel@tonic-gate return; 6497c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate else if (pid > 0) 6527c478bd9Sstevel@tonic-gate { 6537c478bd9Sstevel@tonic-gate #if HASFLOCK 6547c478bd9Sstevel@tonic-gate /* be sure we leave the temp files to our child */ 6557c478bd9Sstevel@tonic-gate /* close any random open files in the envelope */ 6567c478bd9Sstevel@tonic-gate closexscript(e); 6577c478bd9Sstevel@tonic-gate if (e->e_dfp != NULL) 6587c478bd9Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 6597c478bd9Sstevel@tonic-gate e->e_dfp = NULL; 6607c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate /* can't call unlockqueue to avoid unlink of xfp */ 6637c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 6647c478bd9Sstevel@tonic-gate (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 6657c478bd9Sstevel@tonic-gate else 6667c478bd9Sstevel@tonic-gate syserr("%s: sendall: null lockfp", e->e_id); 6677c478bd9Sstevel@tonic-gate e->e_lockfp = NULL; 6687c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate /* make sure the parent doesn't own the envelope */ 6717c478bd9Sstevel@tonic-gate e->e_id = NULL; 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK 6747c478bd9Sstevel@tonic-gate /* catch intermediate zombie */ 6757c478bd9Sstevel@tonic-gate (void) waitfor(pid); 6767c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */ 6777c478bd9Sstevel@tonic-gate return; 6787c478bd9Sstevel@tonic-gate } 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate /* Reset global flags */ 6817c478bd9Sstevel@tonic-gate RestartRequest = NULL; 6827c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 6837c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 6847c478bd9Sstevel@tonic-gate PendingSignal = 0; 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate /* 6877c478bd9Sstevel@tonic-gate ** Initialize exception stack and default exception 6887c478bd9Sstevel@tonic-gate ** handler for child process. 6897c478bd9Sstevel@tonic-gate */ 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate sm_exc_newthread(fatal_error); 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate /* 6947c478bd9Sstevel@tonic-gate ** Since we have accepted responsbility for the message, 6957c478bd9Sstevel@tonic-gate ** change the SIGTERM handler. intsig() (the old handler) 6967c478bd9Sstevel@tonic-gate ** would remove the envelope if this was a command line 6977c478bd9Sstevel@tonic-gate ** message submission. 6987c478bd9Sstevel@tonic-gate */ 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK 7037c478bd9Sstevel@tonic-gate /* double fork to avoid zombies */ 7047c478bd9Sstevel@tonic-gate pid = fork(); 7057c478bd9Sstevel@tonic-gate if (pid > 0) 7067c478bd9Sstevel@tonic-gate exit(EX_OK); 7077c478bd9Sstevel@tonic-gate save_errno = errno; 7087c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */ 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 7117c478bd9Sstevel@tonic-gate 7127c478bd9Sstevel@tonic-gate /* be sure we are immune from the terminal */ 7137c478bd9Sstevel@tonic-gate disconnect(2, e); 7147c478bd9Sstevel@tonic-gate clearstats(); 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate /* prevent parent from waiting if there was an error */ 7177c478bd9Sstevel@tonic-gate if (pid < 0) 7187c478bd9Sstevel@tonic-gate { 7197c478bd9Sstevel@tonic-gate errno = save_errno; 7207c478bd9Sstevel@tonic-gate syserr("deliver: fork 2"); 7217c478bd9Sstevel@tonic-gate #if HASFLOCK 7227c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 7237c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 7247c478bd9Sstevel@tonic-gate e->e_id = NULL; 7257c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 7267c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate 7297c478bd9Sstevel@tonic-gate /* be sure to give error messages in child */ 7307c478bd9Sstevel@tonic-gate QuickAbort = false; 7317c478bd9Sstevel@tonic-gate 7327c478bd9Sstevel@tonic-gate /* 7337c478bd9Sstevel@tonic-gate ** Close any cached connections. 7347c478bd9Sstevel@tonic-gate ** 7357c478bd9Sstevel@tonic-gate ** We don't send the QUIT protocol because the parent 7367c478bd9Sstevel@tonic-gate ** still knows about the connection. 7377c478bd9Sstevel@tonic-gate ** 7387c478bd9Sstevel@tonic-gate ** This should only happen when delivering an error 7397c478bd9Sstevel@tonic-gate ** message. 7407c478bd9Sstevel@tonic-gate */ 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate mci_flush(false, NULL); 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate #if HASFLOCK 7457c478bd9Sstevel@tonic-gate break; 7467c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 7477c478bd9Sstevel@tonic-gate 7487c478bd9Sstevel@tonic-gate /* 7497c478bd9Sstevel@tonic-gate ** Now reacquire and run the various queue files. 7507c478bd9Sstevel@tonic-gate */ 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 7537c478bd9Sstevel@tonic-gate { 7547c478bd9Sstevel@tonic-gate ENVELOPE *sibling = ee->e_sibling; 7557c478bd9Sstevel@tonic-gate 7567c478bd9Sstevel@tonic-gate (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 7577c478bd9Sstevel@tonic-gate false, false, ee); 7587c478bd9Sstevel@tonic-gate ee->e_sibling = sibling; 7597c478bd9Sstevel@tonic-gate } 7607c478bd9Sstevel@tonic-gate (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 7617c478bd9Sstevel@tonic-gate false, false, e); 7627c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 7637c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 7647c478bd9Sstevel@tonic-gate } 7657c478bd9Sstevel@tonic-gate 7667c478bd9Sstevel@tonic-gate sendenvelope(e, mode); 7677c478bd9Sstevel@tonic-gate dropenvelope(e, true, true); 7687c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 7697c478bd9Sstevel@tonic-gate { 7707c478bd9Sstevel@tonic-gate CurEnv = ee; 7717c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY) 7727c478bd9Sstevel@tonic-gate openxscript(ee); 7737c478bd9Sstevel@tonic-gate sendenvelope(ee, mode); 7747c478bd9Sstevel@tonic-gate dropenvelope(ee, true, true); 7757c478bd9Sstevel@tonic-gate } 7767c478bd9Sstevel@tonic-gate CurEnv = e; 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate Verbose = oldverbose; 7797c478bd9Sstevel@tonic-gate if (mode == SM_FORK) 7807c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 7817c478bd9Sstevel@tonic-gate } 7827c478bd9Sstevel@tonic-gate 7837c478bd9Sstevel@tonic-gate static void 7847c478bd9Sstevel@tonic-gate sendenvelope(e, mode) 7857c478bd9Sstevel@tonic-gate register ENVELOPE *e; 7867c478bd9Sstevel@tonic-gate int mode; 7877c478bd9Sstevel@tonic-gate { 7887c478bd9Sstevel@tonic-gate register ADDRESS *q; 7897c478bd9Sstevel@tonic-gate bool didany; 7907c478bd9Sstevel@tonic-gate 7917c478bd9Sstevel@tonic-gate if (tTd(13, 10)) 7927c478bd9Sstevel@tonic-gate sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 7937c478bd9Sstevel@tonic-gate e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 7947c478bd9Sstevel@tonic-gate e->e_flags); 7957c478bd9Sstevel@tonic-gate if (LogLevel > 80) 7967c478bd9Sstevel@tonic-gate sm_syslog(LOG_DEBUG, e->e_id, 7977c478bd9Sstevel@tonic-gate "sendenvelope, flags=0x%lx", 7987c478bd9Sstevel@tonic-gate e->e_flags); 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate /* 8017c478bd9Sstevel@tonic-gate ** If we have had global, fatal errors, don't bother sending 8027c478bd9Sstevel@tonic-gate ** the message at all if we are in SMTP mode. Local errors 8037c478bd9Sstevel@tonic-gate ** (e.g., a single address failing) will still cause the other 8047c478bd9Sstevel@tonic-gate ** addresses to be sent. 8057c478bd9Sstevel@tonic-gate */ 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate if (bitset(EF_FATALERRS, e->e_flags) && 8087c478bd9Sstevel@tonic-gate (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 8097c478bd9Sstevel@tonic-gate { 8107c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 8117c478bd9Sstevel@tonic-gate return; 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate 8147c478bd9Sstevel@tonic-gate /* 8157c478bd9Sstevel@tonic-gate ** Don't attempt deliveries if we want to bounce now 8167c478bd9Sstevel@tonic-gate ** or if deliver-by time is exceeded. 8177c478bd9Sstevel@tonic-gate */ 8187c478bd9Sstevel@tonic-gate 8197c478bd9Sstevel@tonic-gate if (!bitset(EF_RESPONSE, e->e_flags) && 8207c478bd9Sstevel@tonic-gate (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 8217c478bd9Sstevel@tonic-gate (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 8227c478bd9Sstevel@tonic-gate curtime() > e->e_ctime + e->e_deliver_by))) 8237c478bd9Sstevel@tonic-gate return; 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate /* 8267c478bd9Sstevel@tonic-gate ** Run through the list and send everything. 8277c478bd9Sstevel@tonic-gate ** 8287c478bd9Sstevel@tonic-gate ** Set EF_GLOBALERRS so that error messages during delivery 8297c478bd9Sstevel@tonic-gate ** result in returned mail. 8307c478bd9Sstevel@tonic-gate */ 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate e->e_nsent = 0; 8337c478bd9Sstevel@tonic-gate e->e_flags |= EF_GLOBALERRS; 8347c478bd9Sstevel@tonic-gate 8357c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 8367c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 8377c478bd9Sstevel@tonic-gate didany = false; 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate if (!bitset(EF_SPLIT, e->e_flags)) 8407c478bd9Sstevel@tonic-gate { 8417c478bd9Sstevel@tonic-gate ENVELOPE *oldsib; 8427c478bd9Sstevel@tonic-gate ENVELOPE *ee; 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate /* 8457c478bd9Sstevel@tonic-gate ** Save old sibling and set it to NULL to avoid 8467c478bd9Sstevel@tonic-gate ** queueing up the same envelopes again. 8477c478bd9Sstevel@tonic-gate ** This requires that envelopes in that list have 8487c478bd9Sstevel@tonic-gate ** been take care of before (or at some other place). 8497c478bd9Sstevel@tonic-gate */ 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate oldsib = e->e_sibling; 8527c478bd9Sstevel@tonic-gate e->e_sibling = NULL; 8537c478bd9Sstevel@tonic-gate if (!split_by_recipient(e) && 8547c478bd9Sstevel@tonic-gate bitset(EF_FATALERRS, e->e_flags)) 8557c478bd9Sstevel@tonic-gate { 8567c478bd9Sstevel@tonic-gate if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 8577c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 8587c478bd9Sstevel@tonic-gate return; 8597c478bd9Sstevel@tonic-gate } 8607c478bd9Sstevel@tonic-gate for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 8617c478bd9Sstevel@tonic-gate queueup(ee, false, true); 8627c478bd9Sstevel@tonic-gate 8637c478bd9Sstevel@tonic-gate /* clean up */ 8647c478bd9Sstevel@tonic-gate for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 8657c478bd9Sstevel@tonic-gate { 8667c478bd9Sstevel@tonic-gate /* now unlock the job */ 8677c478bd9Sstevel@tonic-gate closexscript(ee); 8687c478bd9Sstevel@tonic-gate unlockqueue(ee); 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate /* this envelope is marked unused */ 8717c478bd9Sstevel@tonic-gate if (ee->e_dfp != NULL) 8727c478bd9Sstevel@tonic-gate { 8737c478bd9Sstevel@tonic-gate (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 8747c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate ee->e_id = NULL; 8777c478bd9Sstevel@tonic-gate ee->e_flags &= ~EF_HAS_DF; 8787c478bd9Sstevel@tonic-gate } 8797c478bd9Sstevel@tonic-gate e->e_sibling = oldsib; 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate 8827c478bd9Sstevel@tonic-gate /* now run through the queue */ 8837c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 8847c478bd9Sstevel@tonic-gate { 8857c478bd9Sstevel@tonic-gate #if XDEBUG 8867c478bd9Sstevel@tonic-gate char wbuf[MAXNAME + 20]; 8877c478bd9Sstevel@tonic-gate 888*058561cbSjbeck (void) sm_snprintf(wbuf, sizeof(wbuf), "sendall(%.*s)", 8897c478bd9Sstevel@tonic-gate MAXNAME, q->q_paddr); 8907c478bd9Sstevel@tonic-gate checkfd012(wbuf); 8917c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 8927c478bd9Sstevel@tonic-gate if (mode == SM_VERIFY) 8937c478bd9Sstevel@tonic-gate { 8947c478bd9Sstevel@tonic-gate e->e_to = q->q_paddr; 8957c478bd9Sstevel@tonic-gate if (QS_IS_SENDABLE(q->q_state)) 8967c478bd9Sstevel@tonic-gate { 8977c478bd9Sstevel@tonic-gate if (q->q_host != NULL && q->q_host[0] != '\0') 8987c478bd9Sstevel@tonic-gate message("deliverable: mailer %s, host %s, user %s", 8997c478bd9Sstevel@tonic-gate q->q_mailer->m_name, 9007c478bd9Sstevel@tonic-gate q->q_host, 9017c478bd9Sstevel@tonic-gate q->q_user); 9027c478bd9Sstevel@tonic-gate else 9037c478bd9Sstevel@tonic-gate message("deliverable: mailer %s, user %s", 9047c478bd9Sstevel@tonic-gate q->q_mailer->m_name, 9057c478bd9Sstevel@tonic-gate q->q_user); 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate } 9087c478bd9Sstevel@tonic-gate else if (QS_IS_OK(q->q_state)) 9097c478bd9Sstevel@tonic-gate { 9107c478bd9Sstevel@tonic-gate /* 9117c478bd9Sstevel@tonic-gate ** Checkpoint the send list every few addresses 9127c478bd9Sstevel@tonic-gate */ 9137c478bd9Sstevel@tonic-gate 9147c478bd9Sstevel@tonic-gate if (CheckpointInterval > 0 && 9157c478bd9Sstevel@tonic-gate e->e_nsent >= CheckpointInterval) 9167c478bd9Sstevel@tonic-gate { 9177c478bd9Sstevel@tonic-gate queueup(e, false, false); 9187c478bd9Sstevel@tonic-gate e->e_nsent = 0; 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate (void) deliver(e, q); 9217c478bd9Sstevel@tonic-gate didany = true; 9227c478bd9Sstevel@tonic-gate } 9237c478bd9Sstevel@tonic-gate } 9247c478bd9Sstevel@tonic-gate if (didany) 9257c478bd9Sstevel@tonic-gate { 9267c478bd9Sstevel@tonic-gate e->e_dtime = curtime(); 9277c478bd9Sstevel@tonic-gate e->e_ntries++; 9287c478bd9Sstevel@tonic-gate } 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate #if XDEBUG 9317c478bd9Sstevel@tonic-gate checkfd012("end of sendenvelope"); 9327c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 9337c478bd9Sstevel@tonic-gate } 9347c478bd9Sstevel@tonic-gate 9357c478bd9Sstevel@tonic-gate #if REQUIRES_DIR_FSYNC 9367c478bd9Sstevel@tonic-gate /* 9377c478bd9Sstevel@tonic-gate ** SYNC_DIR -- fsync a directory based on a filename 9387c478bd9Sstevel@tonic-gate ** 9397c478bd9Sstevel@tonic-gate ** Parameters: 9407c478bd9Sstevel@tonic-gate ** filename -- path of file 9417c478bd9Sstevel@tonic-gate ** panic -- panic? 9427c478bd9Sstevel@tonic-gate ** 9437c478bd9Sstevel@tonic-gate ** Returns: 9447c478bd9Sstevel@tonic-gate ** none 9457c478bd9Sstevel@tonic-gate */ 9467c478bd9Sstevel@tonic-gate 9477c478bd9Sstevel@tonic-gate void 9487c478bd9Sstevel@tonic-gate sync_dir(filename, panic) 9497c478bd9Sstevel@tonic-gate char *filename; 9507c478bd9Sstevel@tonic-gate bool panic; 9517c478bd9Sstevel@tonic-gate { 9527c478bd9Sstevel@tonic-gate int dirfd; 9537c478bd9Sstevel@tonic-gate char *dirp; 9547c478bd9Sstevel@tonic-gate char dir[MAXPATHLEN]; 9557c478bd9Sstevel@tonic-gate 9567c478bd9Sstevel@tonic-gate if (!RequiresDirfsync) 9577c478bd9Sstevel@tonic-gate return; 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate /* filesystems which require the directory be synced */ 9607c478bd9Sstevel@tonic-gate dirp = strrchr(filename, '/'); 9617c478bd9Sstevel@tonic-gate if (dirp != NULL) 9627c478bd9Sstevel@tonic-gate { 963*058561cbSjbeck if (sm_strlcpy(dir, filename, sizeof(dir)) >= sizeof(dir)) 9647c478bd9Sstevel@tonic-gate return; 9657c478bd9Sstevel@tonic-gate dir[dirp - filename] = '\0'; 9667c478bd9Sstevel@tonic-gate dirp = dir; 9677c478bd9Sstevel@tonic-gate } 9687c478bd9Sstevel@tonic-gate else 9697c478bd9Sstevel@tonic-gate dirp = "."; 9707c478bd9Sstevel@tonic-gate dirfd = open(dirp, O_RDONLY, 0700); 9717c478bd9Sstevel@tonic-gate if (tTd(40,32)) 9727c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 9737c478bd9Sstevel@tonic-gate dirp, dirfd); 9747c478bd9Sstevel@tonic-gate if (dirfd >= 0) 9757c478bd9Sstevel@tonic-gate { 9767c478bd9Sstevel@tonic-gate if (fsync(dirfd) < 0) 9777c478bd9Sstevel@tonic-gate { 9787c478bd9Sstevel@tonic-gate if (panic) 9797c478bd9Sstevel@tonic-gate syserr("!sync_dir: cannot fsync directory %s", 9807c478bd9Sstevel@tonic-gate dirp); 9817c478bd9Sstevel@tonic-gate else if (LogLevel > 1) 9827c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 9837c478bd9Sstevel@tonic-gate "sync_dir: cannot fsync directory %s: %s", 9847c478bd9Sstevel@tonic-gate dirp, sm_errstring(errno)); 9857c478bd9Sstevel@tonic-gate } 9867c478bd9Sstevel@tonic-gate (void) close(dirfd); 9877c478bd9Sstevel@tonic-gate } 9887c478bd9Sstevel@tonic-gate } 9897c478bd9Sstevel@tonic-gate #endif /* REQUIRES_DIR_FSYNC */ 9907c478bd9Sstevel@tonic-gate /* 9917c478bd9Sstevel@tonic-gate ** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 9927c478bd9Sstevel@tonic-gate ** 9937c478bd9Sstevel@tonic-gate ** Parameters: 9947c478bd9Sstevel@tonic-gate ** e -- the existing envelope 9957c478bd9Sstevel@tonic-gate ** ee -- the new envelope 9967c478bd9Sstevel@tonic-gate ** type -- the queue file type (e.g., DATAFL_LETTER) 9977c478bd9Sstevel@tonic-gate ** 9987c478bd9Sstevel@tonic-gate ** Returns: 9997c478bd9Sstevel@tonic-gate ** none 10007c478bd9Sstevel@tonic-gate */ 10017c478bd9Sstevel@tonic-gate 10027c478bd9Sstevel@tonic-gate static void 10037c478bd9Sstevel@tonic-gate dup_queue_file(e, ee, type) 10047c478bd9Sstevel@tonic-gate ENVELOPE *e, *ee; 10057c478bd9Sstevel@tonic-gate int type; 10067c478bd9Sstevel@tonic-gate { 10077c478bd9Sstevel@tonic-gate char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 10087c478bd9Sstevel@tonic-gate 10097c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 10107c478bd9Sstevel@tonic-gate ee->e_xfp = NULL; 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate /* 10137c478bd9Sstevel@tonic-gate ** Make sure both are in the same directory. 10147c478bd9Sstevel@tonic-gate */ 10157c478bd9Sstevel@tonic-gate 1016*058561cbSjbeck (void) sm_strlcpy(f1buf, queuename(e, type), sizeof(f1buf)); 1017*058561cbSjbeck (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof(f2buf)); 10187c478bd9Sstevel@tonic-gate 10197c478bd9Sstevel@tonic-gate /* Force the df to disk if it's not there yet */ 10207c478bd9Sstevel@tonic-gate if (type == DATAFL_LETTER && e->e_dfp != NULL && 10217c478bd9Sstevel@tonic-gate sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 10227c478bd9Sstevel@tonic-gate errno != EINVAL) 10237c478bd9Sstevel@tonic-gate { 10247c478bd9Sstevel@tonic-gate syserr("!dup_queue_file: can't commit %s", f1buf); 10257c478bd9Sstevel@tonic-gate /* NOTREACHED */ 10267c478bd9Sstevel@tonic-gate } 10277c478bd9Sstevel@tonic-gate 10287c478bd9Sstevel@tonic-gate if (link(f1buf, f2buf) < 0) 10297c478bd9Sstevel@tonic-gate { 10307c478bd9Sstevel@tonic-gate int save_errno = errno; 10317c478bd9Sstevel@tonic-gate 10327c478bd9Sstevel@tonic-gate syserr("sendall: link(%s, %s)", f1buf, f2buf); 10337c478bd9Sstevel@tonic-gate if (save_errno == EEXIST) 10347c478bd9Sstevel@tonic-gate { 10357c478bd9Sstevel@tonic-gate if (unlink(f2buf) < 0) 10367c478bd9Sstevel@tonic-gate { 10377c478bd9Sstevel@tonic-gate syserr("!sendall: unlink(%s): permanent", 10387c478bd9Sstevel@tonic-gate f2buf); 10397c478bd9Sstevel@tonic-gate /* NOTREACHED */ 10407c478bd9Sstevel@tonic-gate } 10417c478bd9Sstevel@tonic-gate if (link(f1buf, f2buf) < 0) 10427c478bd9Sstevel@tonic-gate { 10437c478bd9Sstevel@tonic-gate syserr("!sendall: link(%s, %s): permanent", 10447c478bd9Sstevel@tonic-gate f1buf, f2buf); 10457c478bd9Sstevel@tonic-gate /* NOTREACHED */ 10467c478bd9Sstevel@tonic-gate } 10477c478bd9Sstevel@tonic-gate } 10487c478bd9Sstevel@tonic-gate } 10497c478bd9Sstevel@tonic-gate SYNC_DIR(f2buf, true); 10507c478bd9Sstevel@tonic-gate } 10517c478bd9Sstevel@tonic-gate /* 10527c478bd9Sstevel@tonic-gate ** DOFORK -- do a fork, retrying a couple of times on failure. 10537c478bd9Sstevel@tonic-gate ** 10547c478bd9Sstevel@tonic-gate ** This MUST be a macro, since after a vfork we are running 10557c478bd9Sstevel@tonic-gate ** two processes on the same stack!!! 10567c478bd9Sstevel@tonic-gate ** 10577c478bd9Sstevel@tonic-gate ** Parameters: 10587c478bd9Sstevel@tonic-gate ** none. 10597c478bd9Sstevel@tonic-gate ** 10607c478bd9Sstevel@tonic-gate ** Returns: 10617c478bd9Sstevel@tonic-gate ** From a macro??? You've got to be kidding! 10627c478bd9Sstevel@tonic-gate ** 10637c478bd9Sstevel@tonic-gate ** Side Effects: 10647c478bd9Sstevel@tonic-gate ** Modifies the ==> LOCAL <== variable 'pid', leaving: 10657c478bd9Sstevel@tonic-gate ** pid of child in parent, zero in child. 10667c478bd9Sstevel@tonic-gate ** -1 on unrecoverable error. 10677c478bd9Sstevel@tonic-gate ** 10687c478bd9Sstevel@tonic-gate ** Notes: 10697c478bd9Sstevel@tonic-gate ** I'm awfully sorry this looks so awful. That's 10707c478bd9Sstevel@tonic-gate ** vfork for you..... 10717c478bd9Sstevel@tonic-gate */ 10727c478bd9Sstevel@tonic-gate 10737c478bd9Sstevel@tonic-gate #define NFORKTRIES 5 10747c478bd9Sstevel@tonic-gate 10757c478bd9Sstevel@tonic-gate #ifndef FORK 10767c478bd9Sstevel@tonic-gate # define FORK fork 10777c478bd9Sstevel@tonic-gate #endif /* ! FORK */ 10787c478bd9Sstevel@tonic-gate 10797c478bd9Sstevel@tonic-gate #define DOFORK(fORKfN) \ 10807c478bd9Sstevel@tonic-gate {\ 10817c478bd9Sstevel@tonic-gate register int i;\ 10827c478bd9Sstevel@tonic-gate \ 10837c478bd9Sstevel@tonic-gate for (i = NFORKTRIES; --i >= 0; )\ 10847c478bd9Sstevel@tonic-gate {\ 10857c478bd9Sstevel@tonic-gate pid = fORKfN();\ 10867c478bd9Sstevel@tonic-gate if (pid >= 0)\ 10877c478bd9Sstevel@tonic-gate break;\ 10887c478bd9Sstevel@tonic-gate if (i > 0)\ 10897c478bd9Sstevel@tonic-gate (void) sleep((unsigned) NFORKTRIES - i);\ 10907c478bd9Sstevel@tonic-gate }\ 10917c478bd9Sstevel@tonic-gate } 10927c478bd9Sstevel@tonic-gate /* 10937c478bd9Sstevel@tonic-gate ** DOFORK -- simple fork interface to DOFORK. 10947c478bd9Sstevel@tonic-gate ** 10957c478bd9Sstevel@tonic-gate ** Parameters: 10967c478bd9Sstevel@tonic-gate ** none. 10977c478bd9Sstevel@tonic-gate ** 10987c478bd9Sstevel@tonic-gate ** Returns: 10997c478bd9Sstevel@tonic-gate ** pid of child in parent. 11007c478bd9Sstevel@tonic-gate ** zero in child. 11017c478bd9Sstevel@tonic-gate ** -1 on error. 11027c478bd9Sstevel@tonic-gate ** 11037c478bd9Sstevel@tonic-gate ** Side Effects: 11047c478bd9Sstevel@tonic-gate ** returns twice, once in parent and once in child. 11057c478bd9Sstevel@tonic-gate */ 11067c478bd9Sstevel@tonic-gate 11077c478bd9Sstevel@tonic-gate pid_t 11087c478bd9Sstevel@tonic-gate dofork() 11097c478bd9Sstevel@tonic-gate { 11107c478bd9Sstevel@tonic-gate register pid_t pid = -1; 11117c478bd9Sstevel@tonic-gate 11127c478bd9Sstevel@tonic-gate DOFORK(fork); 11137c478bd9Sstevel@tonic-gate return pid; 11147c478bd9Sstevel@tonic-gate } 11157c478bd9Sstevel@tonic-gate 11167c478bd9Sstevel@tonic-gate /* 11177c478bd9Sstevel@tonic-gate ** COLONCMP -- compare host-signatures up to first ':' or EOS 11187c478bd9Sstevel@tonic-gate ** 11197c478bd9Sstevel@tonic-gate ** This takes two strings which happen to be host-signatures and 11207c478bd9Sstevel@tonic-gate ** compares them. If the lowest preference portions of the MX-RR's 11217c478bd9Sstevel@tonic-gate ** match (up to ':' or EOS, whichever is first), then we have 11227c478bd9Sstevel@tonic-gate ** match. This is used for coattail-piggybacking messages during 11237c478bd9Sstevel@tonic-gate ** message delivery. 11247c478bd9Sstevel@tonic-gate ** If the signatures are the same up to the first ':' the remainder of 11257c478bd9Sstevel@tonic-gate ** the signatures are then compared with a normal strcmp(). This saves 11267c478bd9Sstevel@tonic-gate ** re-examining the first part of the signatures. 11277c478bd9Sstevel@tonic-gate ** 11287c478bd9Sstevel@tonic-gate ** Parameters: 11297c478bd9Sstevel@tonic-gate ** a - first host-signature 11307c478bd9Sstevel@tonic-gate ** b - second host-signature 11317c478bd9Sstevel@tonic-gate ** 11327c478bd9Sstevel@tonic-gate ** Returns: 11337c478bd9Sstevel@tonic-gate ** HS_MATCH_NO -- no "match". 11347c478bd9Sstevel@tonic-gate ** HS_MATCH_FIRST -- "match" for the first MX preference 11357c478bd9Sstevel@tonic-gate ** (up to the first colon (':')). 11367c478bd9Sstevel@tonic-gate ** HS_MATCH_FULL -- match for the entire MX record. 11377c478bd9Sstevel@tonic-gate ** 11387c478bd9Sstevel@tonic-gate ** Side Effects: 11397c478bd9Sstevel@tonic-gate ** none. 11407c478bd9Sstevel@tonic-gate */ 11417c478bd9Sstevel@tonic-gate 11427c478bd9Sstevel@tonic-gate #define HS_MATCH_NO 0 11437c478bd9Sstevel@tonic-gate #define HS_MATCH_FIRST 1 11447c478bd9Sstevel@tonic-gate #define HS_MATCH_FULL 2 11457c478bd9Sstevel@tonic-gate 11467c478bd9Sstevel@tonic-gate static int 11477c478bd9Sstevel@tonic-gate coloncmp(a, b) 11487c478bd9Sstevel@tonic-gate register const char *a; 11497c478bd9Sstevel@tonic-gate register const char *b; 11507c478bd9Sstevel@tonic-gate { 11517c478bd9Sstevel@tonic-gate int ret = HS_MATCH_NO; 11527c478bd9Sstevel@tonic-gate int braclev = 0; 11537c478bd9Sstevel@tonic-gate 11547c478bd9Sstevel@tonic-gate while (*a == *b++) 11557c478bd9Sstevel@tonic-gate { 11567c478bd9Sstevel@tonic-gate /* Need to account for IPv6 bracketed addresses */ 11577c478bd9Sstevel@tonic-gate if (*a == '[') 11587c478bd9Sstevel@tonic-gate braclev++; 11597c478bd9Sstevel@tonic-gate else if (*a == ']' && braclev > 0) 11607c478bd9Sstevel@tonic-gate braclev--; 11617c478bd9Sstevel@tonic-gate else if (*a == ':' && braclev <= 0) 11627c478bd9Sstevel@tonic-gate { 11637c478bd9Sstevel@tonic-gate ret = HS_MATCH_FIRST; 11647c478bd9Sstevel@tonic-gate a++; 11657c478bd9Sstevel@tonic-gate break; 11667c478bd9Sstevel@tonic-gate } 11677c478bd9Sstevel@tonic-gate else if (*a == '\0') 11687c478bd9Sstevel@tonic-gate return HS_MATCH_FULL; /* a full match */ 11697c478bd9Sstevel@tonic-gate a++; 11707c478bd9Sstevel@tonic-gate } 11717c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_NO && 11727c478bd9Sstevel@tonic-gate braclev <= 0 && 11737c478bd9Sstevel@tonic-gate ((*a == '\0' && *(b - 1) == ':') || 11747c478bd9Sstevel@tonic-gate (*a == ':' && *(b - 1) == '\0'))) 11757c478bd9Sstevel@tonic-gate return HS_MATCH_FIRST; 11767c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 11777c478bd9Sstevel@tonic-gate return HS_MATCH_FULL; 11787c478bd9Sstevel@tonic-gate 11797c478bd9Sstevel@tonic-gate return ret; 11807c478bd9Sstevel@tonic-gate } 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate /* 11837c478bd9Sstevel@tonic-gate ** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 11847c478bd9Sstevel@tonic-gate ** 11857c478bd9Sstevel@tonic-gate ** Parameters: 11867c478bd9Sstevel@tonic-gate ** e -- envelope 11877c478bd9Sstevel@tonic-gate ** tried_fallbacksmarthost -- has been tried already? (in/out) 11887c478bd9Sstevel@tonic-gate ** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 11897c478bd9Sstevel@tonic-gate ** hbsz -- size of hostbuf 11907c478bd9Sstevel@tonic-gate ** status -- current delivery status 11917c478bd9Sstevel@tonic-gate ** 11927c478bd9Sstevel@tonic-gate ** Returns: 11937c478bd9Sstevel@tonic-gate ** true iff FallbackSmartHost should be tried. 11947c478bd9Sstevel@tonic-gate */ 11957c478bd9Sstevel@tonic-gate 1196*058561cbSjbeck static bool should_try_fbsh __P((ENVELOPE *, bool *, char *, size_t, int)); 1197*058561cbSjbeck 11987c478bd9Sstevel@tonic-gate static bool 11997c478bd9Sstevel@tonic-gate should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 12007c478bd9Sstevel@tonic-gate ENVELOPE *e; 12017c478bd9Sstevel@tonic-gate bool *tried_fallbacksmarthost; 12027c478bd9Sstevel@tonic-gate char *hostbuf; 12037c478bd9Sstevel@tonic-gate size_t hbsz; 12047c478bd9Sstevel@tonic-gate int status; 12057c478bd9Sstevel@tonic-gate { 12067c478bd9Sstevel@tonic-gate /* 120749218d4fSjbeck ** If the host was not found or a temporary failure occurred 120849218d4fSjbeck ** and a FallbackSmartHost is defined (and we have not yet 120949218d4fSjbeck ** tried it), then make one last try with it as the host. 12107c478bd9Sstevel@tonic-gate */ 12117c478bd9Sstevel@tonic-gate 121249218d4fSjbeck if ((status == EX_NOHOST || status == EX_TEMPFAIL) && 121349218d4fSjbeck FallbackSmartHost != NULL && !*tried_fallbacksmarthost) 12147c478bd9Sstevel@tonic-gate { 12157c478bd9Sstevel@tonic-gate *tried_fallbacksmarthost = true; 12167c478bd9Sstevel@tonic-gate expand(FallbackSmartHost, hostbuf, hbsz, e); 12177c478bd9Sstevel@tonic-gate if (!wordinclass(hostbuf, 'w')) 12187c478bd9Sstevel@tonic-gate { 12197c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 12207c478bd9Sstevel@tonic-gate sm_dprintf("one last try with FallbackSmartHost %s\n", 12217c478bd9Sstevel@tonic-gate hostbuf); 12227c478bd9Sstevel@tonic-gate return true; 12237c478bd9Sstevel@tonic-gate } 12247c478bd9Sstevel@tonic-gate } 12257c478bd9Sstevel@tonic-gate return false; 12267c478bd9Sstevel@tonic-gate } 12277c478bd9Sstevel@tonic-gate /* 12287c478bd9Sstevel@tonic-gate ** DELIVER -- Deliver a message to a list of addresses. 12297c478bd9Sstevel@tonic-gate ** 12307c478bd9Sstevel@tonic-gate ** This routine delivers to everyone on the same host as the 12317c478bd9Sstevel@tonic-gate ** user on the head of the list. It is clever about mailers 12327c478bd9Sstevel@tonic-gate ** that don't handle multiple users. It is NOT guaranteed 12337c478bd9Sstevel@tonic-gate ** that it will deliver to all these addresses however -- so 12347c478bd9Sstevel@tonic-gate ** deliver should be called once for each address on the 12357c478bd9Sstevel@tonic-gate ** list. 12367c478bd9Sstevel@tonic-gate ** Deliver tries to be as opportunistic as possible about piggybacking 12377c478bd9Sstevel@tonic-gate ** messages. Some definitions to make understanding easier follow below. 12387c478bd9Sstevel@tonic-gate ** Piggybacking occurs when an existing connection to a mail host can 12397c478bd9Sstevel@tonic-gate ** be used to send the same message to more than one recipient at the 12407c478bd9Sstevel@tonic-gate ** same time. So "no piggybacking" means one message for one recipient 12417c478bd9Sstevel@tonic-gate ** per connection. "Intentional piggybacking" happens when the 12427c478bd9Sstevel@tonic-gate ** recipients' host address (not the mail host address) is used to 12437c478bd9Sstevel@tonic-gate ** attempt piggybacking. Recipients with the same host address 12447c478bd9Sstevel@tonic-gate ** have the same mail host. "Coincidental piggybacking" relies on 12457c478bd9Sstevel@tonic-gate ** piggybacking based on all the mail host addresses in the MX-RR. This 12467c478bd9Sstevel@tonic-gate ** is "coincidental" in the fact it could not be predicted until the 12477c478bd9Sstevel@tonic-gate ** MX Resource Records for the hosts were obtained and examined. For 12487c478bd9Sstevel@tonic-gate ** example (preference order and equivalence is important, not values): 12497c478bd9Sstevel@tonic-gate ** domain1 IN MX 10 mxhost-A 12507c478bd9Sstevel@tonic-gate ** IN MX 20 mxhost-B 12517c478bd9Sstevel@tonic-gate ** domain2 IN MX 4 mxhost-A 12527c478bd9Sstevel@tonic-gate ** IN MX 8 mxhost-B 12537c478bd9Sstevel@tonic-gate ** Domain1 and domain2 can piggyback the same message to mxhost-A or 12547c478bd9Sstevel@tonic-gate ** mxhost-B (if mxhost-A cannot be reached). 12557c478bd9Sstevel@tonic-gate ** "Coattail piggybacking" relaxes the strictness of "coincidental 12567c478bd9Sstevel@tonic-gate ** piggybacking" in the hope that most significant (lowest value) 12577c478bd9Sstevel@tonic-gate ** MX preference host(s) can create more piggybacking. For example 12587c478bd9Sstevel@tonic-gate ** (again, preference order and equivalence is important, not values): 12597c478bd9Sstevel@tonic-gate ** domain3 IN MX 100 mxhost-C 12607c478bd9Sstevel@tonic-gate ** IN MX 100 mxhost-D 12617c478bd9Sstevel@tonic-gate ** IN MX 200 mxhost-E 12627c478bd9Sstevel@tonic-gate ** domain4 IN MX 50 mxhost-C 12637c478bd9Sstevel@tonic-gate ** IN MX 50 mxhost-D 12647c478bd9Sstevel@tonic-gate ** IN MX 80 mxhost-F 12657c478bd9Sstevel@tonic-gate ** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 12667c478bd9Sstevel@tonic-gate ** is available. Same with mxhost-D because in both RR's the preference 12677c478bd9Sstevel@tonic-gate ** value is the same as mxhost-C, respectively. 12687c478bd9Sstevel@tonic-gate ** So deliver attempts coattail piggybacking when possible. If the 12697c478bd9Sstevel@tonic-gate ** first MX preference level hosts cannot be used then the piggybacking 12707c478bd9Sstevel@tonic-gate ** reverts to coincidental piggybacking. Using the above example you 12717c478bd9Sstevel@tonic-gate ** cannot deliver to mxhost-F for domain3 regardless of preference value. 12727c478bd9Sstevel@tonic-gate ** ("Coattail" from "riding on the coattails of your predecessor" meaning 12737c478bd9Sstevel@tonic-gate ** gaining benefit from a predecessor effort with no or little addition 12747c478bd9Sstevel@tonic-gate ** effort. The predecessor here being the preceding MX RR). 12757c478bd9Sstevel@tonic-gate ** 12767c478bd9Sstevel@tonic-gate ** Parameters: 12777c478bd9Sstevel@tonic-gate ** e -- the envelope to deliver. 12787c478bd9Sstevel@tonic-gate ** firstto -- head of the address list to deliver to. 12797c478bd9Sstevel@tonic-gate ** 12807c478bd9Sstevel@tonic-gate ** Returns: 12817c478bd9Sstevel@tonic-gate ** zero -- successfully delivered. 12827c478bd9Sstevel@tonic-gate ** else -- some failure, see ExitStat for more info. 12837c478bd9Sstevel@tonic-gate ** 12847c478bd9Sstevel@tonic-gate ** Side Effects: 12857c478bd9Sstevel@tonic-gate ** The standard input is passed off to someone. 12867c478bd9Sstevel@tonic-gate */ 12877c478bd9Sstevel@tonic-gate 12887c478bd9Sstevel@tonic-gate static int 12897c478bd9Sstevel@tonic-gate deliver(e, firstto) 12907c478bd9Sstevel@tonic-gate register ENVELOPE *e; 12917c478bd9Sstevel@tonic-gate ADDRESS *firstto; 12927c478bd9Sstevel@tonic-gate { 12937c478bd9Sstevel@tonic-gate char *host; /* host being sent to */ 12947c478bd9Sstevel@tonic-gate char *user; /* user being sent to */ 12957c478bd9Sstevel@tonic-gate char **pvp; 12967c478bd9Sstevel@tonic-gate register char **mvp; 12977c478bd9Sstevel@tonic-gate register char *p; 12987c478bd9Sstevel@tonic-gate register MAILER *m; /* mailer for this recipient */ 12997c478bd9Sstevel@tonic-gate ADDRESS *volatile ctladdr; 13007c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT 13017c478bd9Sstevel@tonic-gate ADDRESS *volatile contextaddr = NULL; 13027c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */ 13037c478bd9Sstevel@tonic-gate register MCI *volatile mci; 13047c478bd9Sstevel@tonic-gate register ADDRESS *SM_NONVOLATILE to = firstto; 13057c478bd9Sstevel@tonic-gate volatile bool clever = false; /* running user smtp to this mailer */ 13067c478bd9Sstevel@tonic-gate ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 13077c478bd9Sstevel@tonic-gate int rcode; /* response code */ 13087c478bd9Sstevel@tonic-gate SM_NONVOLATILE int lmtp_rcode = EX_OK; 13097c478bd9Sstevel@tonic-gate SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 13107c478bd9Sstevel@tonic-gate SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 13117c478bd9Sstevel@tonic-gate char *firstsig; /* signature of firstto */ 13127c478bd9Sstevel@tonic-gate volatile pid_t pid = -1; 13137c478bd9Sstevel@tonic-gate char *volatile curhost; 13147c478bd9Sstevel@tonic-gate SM_NONVOLATILE unsigned short port = 0; 13157c478bd9Sstevel@tonic-gate SM_NONVOLATILE time_t enough = 0; 13167c478bd9Sstevel@tonic-gate #if NETUNIX 13177c478bd9Sstevel@tonic-gate char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 13187c478bd9Sstevel@tonic-gate #endif /* NETUNIX */ 13197c478bd9Sstevel@tonic-gate time_t xstart; 13207c478bd9Sstevel@tonic-gate bool suidwarn; 13217c478bd9Sstevel@tonic-gate bool anyok; /* at least one address was OK */ 13227c478bd9Sstevel@tonic-gate SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 13237c478bd9Sstevel@tonic-gate bool ovr; 13247c478bd9Sstevel@tonic-gate bool quarantine; 13257c478bd9Sstevel@tonic-gate int strsize; 13267c478bd9Sstevel@tonic-gate int rcptcount; 13277c478bd9Sstevel@tonic-gate int ret; 13287c478bd9Sstevel@tonic-gate static int tobufsize = 0; 13297c478bd9Sstevel@tonic-gate static char *tobuf = NULL; 13307c478bd9Sstevel@tonic-gate char *rpath; /* translated return path */ 13317c478bd9Sstevel@tonic-gate int mpvect[2]; 13327c478bd9Sstevel@tonic-gate int rpvect[2]; 13337c478bd9Sstevel@tonic-gate char *mxhosts[MAXMXHOSTS + 1]; 13347c478bd9Sstevel@tonic-gate char *pv[MAXPV + 1]; 13357c478bd9Sstevel@tonic-gate char buf[MAXNAME + 1]; 13367c478bd9Sstevel@tonic-gate char cbuf[MAXPATHLEN]; 13377c478bd9Sstevel@tonic-gate 13387c478bd9Sstevel@tonic-gate errno = 0; 13397c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 13407c478bd9Sstevel@tonic-gate return 0; 13417c478bd9Sstevel@tonic-gate 13427c478bd9Sstevel@tonic-gate suidwarn = geteuid() == 0; 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate m = to->q_mailer; 13457c478bd9Sstevel@tonic-gate host = to->q_host; 13467c478bd9Sstevel@tonic-gate CurEnv = e; /* just in case */ 13477c478bd9Sstevel@tonic-gate e->e_statmsg = NULL; 13487c478bd9Sstevel@tonic-gate SmtpError[0] = '\0'; 13497c478bd9Sstevel@tonic-gate xstart = curtime(); 13507c478bd9Sstevel@tonic-gate 13517c478bd9Sstevel@tonic-gate if (tTd(10, 1)) 13527c478bd9Sstevel@tonic-gate sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 13537c478bd9Sstevel@tonic-gate e->e_id, m->m_name, host, to->q_user); 13547c478bd9Sstevel@tonic-gate if (tTd(10, 100)) 13557c478bd9Sstevel@tonic-gate printopenfds(false); 13567c478bd9Sstevel@tonic-gate 13577c478bd9Sstevel@tonic-gate /* 13587c478bd9Sstevel@tonic-gate ** Clear {client_*} macros if this is a bounce message to 13597c478bd9Sstevel@tonic-gate ** prevent rejection by check_compat ruleset. 13607c478bd9Sstevel@tonic-gate */ 13617c478bd9Sstevel@tonic-gate 13627c478bd9Sstevel@tonic-gate if (bitset(EF_RESPONSE, e->e_flags)) 13637c478bd9Sstevel@tonic-gate { 13647c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 13657c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 13667c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 13677c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 13687c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 13697c478bd9Sstevel@tonic-gate } 13707c478bd9Sstevel@tonic-gate 13717c478bd9Sstevel@tonic-gate SM_TRY 13727c478bd9Sstevel@tonic-gate { 13737c478bd9Sstevel@tonic-gate ADDRESS *skip_back = NULL; 13747c478bd9Sstevel@tonic-gate 13757c478bd9Sstevel@tonic-gate /* 13767c478bd9Sstevel@tonic-gate ** Do initial argv setup. 13777c478bd9Sstevel@tonic-gate ** Insert the mailer name. Notice that $x expansion is 13787c478bd9Sstevel@tonic-gate ** NOT done on the mailer name. Then, if the mailer has 13797c478bd9Sstevel@tonic-gate ** a picky -f flag, we insert it as appropriate. This 13807c478bd9Sstevel@tonic-gate ** code does not check for 'pv' overflow; this places a 13817c478bd9Sstevel@tonic-gate ** manifest lower limit of 4 for MAXPV. 13827c478bd9Sstevel@tonic-gate ** The from address rewrite is expected to make 13837c478bd9Sstevel@tonic-gate ** the address relative to the other end. 13847c478bd9Sstevel@tonic-gate */ 13857c478bd9Sstevel@tonic-gate 13867c478bd9Sstevel@tonic-gate /* rewrite from address, using rewriting rules */ 13877c478bd9Sstevel@tonic-gate rcode = EX_OK; 13887c478bd9Sstevel@tonic-gate if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 13897c478bd9Sstevel@tonic-gate p = e->e_sender; 13907c478bd9Sstevel@tonic-gate else 13917c478bd9Sstevel@tonic-gate p = e->e_from.q_paddr; 13927c478bd9Sstevel@tonic-gate rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 13937c478bd9Sstevel@tonic-gate if (strlen(rpath) > MAXSHORTSTR) 13947c478bd9Sstevel@tonic-gate { 13957c478bd9Sstevel@tonic-gate rpath = shortenstring(rpath, MAXSHORTSTR); 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate /* avoid bogus errno */ 13987c478bd9Sstevel@tonic-gate errno = 0; 13997c478bd9Sstevel@tonic-gate syserr("remotename: huge return path %s", rpath); 14007c478bd9Sstevel@tonic-gate } 14017c478bd9Sstevel@tonic-gate rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 14027c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'g', rpath); 14037c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'h', host); 14047c478bd9Sstevel@tonic-gate Errors = 0; 14057c478bd9Sstevel@tonic-gate pvp = pv; 14067c478bd9Sstevel@tonic-gate *pvp++ = m->m_argv[0]; 14077c478bd9Sstevel@tonic-gate 14087c478bd9Sstevel@tonic-gate /* ignore long term host status information if mailer flag W is set */ 14097c478bd9Sstevel@tonic-gate if (bitnset(M_NOHOSTSTAT, m->m_flags)) 14107c478bd9Sstevel@tonic-gate IgnoreHostStatus = true; 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate /* insert -f or -r flag as appropriate */ 14137c478bd9Sstevel@tonic-gate if (FromFlag && 14147c478bd9Sstevel@tonic-gate (bitnset(M_FOPT, m->m_flags) || 14157c478bd9Sstevel@tonic-gate bitnset(M_ROPT, m->m_flags))) 14167c478bd9Sstevel@tonic-gate { 14177c478bd9Sstevel@tonic-gate if (bitnset(M_FOPT, m->m_flags)) 14187c478bd9Sstevel@tonic-gate *pvp++ = "-f"; 14197c478bd9Sstevel@tonic-gate else 14207c478bd9Sstevel@tonic-gate *pvp++ = "-r"; 14217c478bd9Sstevel@tonic-gate *pvp++ = rpath; 14227c478bd9Sstevel@tonic-gate } 14237c478bd9Sstevel@tonic-gate 14247c478bd9Sstevel@tonic-gate /* 14257c478bd9Sstevel@tonic-gate ** Append the other fixed parts of the argv. These run 14267c478bd9Sstevel@tonic-gate ** up to the first entry containing "$u". There can only 14277c478bd9Sstevel@tonic-gate ** be one of these, and there are only a few more slots 14287c478bd9Sstevel@tonic-gate ** in the pv after it. 14297c478bd9Sstevel@tonic-gate */ 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 14327c478bd9Sstevel@tonic-gate { 14337c478bd9Sstevel@tonic-gate /* can't use strchr here because of sign extension problems */ 14347c478bd9Sstevel@tonic-gate while (*p != '\0') 14357c478bd9Sstevel@tonic-gate { 14367c478bd9Sstevel@tonic-gate if ((*p++ & 0377) == MACROEXPAND) 14377c478bd9Sstevel@tonic-gate { 14387c478bd9Sstevel@tonic-gate if (*p == 'u') 14397c478bd9Sstevel@tonic-gate break; 14407c478bd9Sstevel@tonic-gate } 14417c478bd9Sstevel@tonic-gate } 14427c478bd9Sstevel@tonic-gate 14437c478bd9Sstevel@tonic-gate if (*p != '\0') 14447c478bd9Sstevel@tonic-gate break; 14457c478bd9Sstevel@tonic-gate 14467c478bd9Sstevel@tonic-gate /* this entry is safe -- go ahead and process it */ 1447*058561cbSjbeck expand(*mvp, buf, sizeof(buf), e); 14487c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 14497c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV - 3]) 14507c478bd9Sstevel@tonic-gate { 14517c478bd9Sstevel@tonic-gate syserr("554 5.3.5 Too many parameters to %s before $u", 14527c478bd9Sstevel@tonic-gate pv[0]); 14537c478bd9Sstevel@tonic-gate rcode = -1; 14547c478bd9Sstevel@tonic-gate goto cleanup; 14557c478bd9Sstevel@tonic-gate } 14567c478bd9Sstevel@tonic-gate } 14577c478bd9Sstevel@tonic-gate 14587c478bd9Sstevel@tonic-gate /* 14597c478bd9Sstevel@tonic-gate ** If we have no substitution for the user name in the argument 14607c478bd9Sstevel@tonic-gate ** list, we know that we must supply the names otherwise -- and 14617c478bd9Sstevel@tonic-gate ** SMTP is the answer!! 14627c478bd9Sstevel@tonic-gate */ 14637c478bd9Sstevel@tonic-gate 14647c478bd9Sstevel@tonic-gate if (*mvp == NULL) 14657c478bd9Sstevel@tonic-gate { 14667c478bd9Sstevel@tonic-gate /* running LMTP or SMTP */ 14677c478bd9Sstevel@tonic-gate clever = true; 14687c478bd9Sstevel@tonic-gate *pvp = NULL; 14697c478bd9Sstevel@tonic-gate } 14707c478bd9Sstevel@tonic-gate else if (bitnset(M_LMTP, m->m_flags)) 14717c478bd9Sstevel@tonic-gate { 14727c478bd9Sstevel@tonic-gate /* not running LMTP */ 14737c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NULL, 14747c478bd9Sstevel@tonic-gate "Warning: mailer %s: LMTP flag (F=z) turned off", 14757c478bd9Sstevel@tonic-gate m->m_name); 14767c478bd9Sstevel@tonic-gate clrbitn(M_LMTP, m->m_flags); 14777c478bd9Sstevel@tonic-gate } 14787c478bd9Sstevel@tonic-gate 14797c478bd9Sstevel@tonic-gate /* 14807c478bd9Sstevel@tonic-gate ** At this point *mvp points to the argument with $u. We 14817c478bd9Sstevel@tonic-gate ** run through our address list and append all the addresses 14827c478bd9Sstevel@tonic-gate ** we can. If we run out of space, do not fret! We can 14837c478bd9Sstevel@tonic-gate ** always send another copy later. 14847c478bd9Sstevel@tonic-gate */ 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate e->e_to = NULL; 14877c478bd9Sstevel@tonic-gate strsize = 2; 14887c478bd9Sstevel@tonic-gate rcptcount = 0; 14897c478bd9Sstevel@tonic-gate ctladdr = NULL; 14907c478bd9Sstevel@tonic-gate if (firstto->q_signature == NULL) 14917c478bd9Sstevel@tonic-gate firstto->q_signature = hostsignature(firstto->q_mailer, 14927c478bd9Sstevel@tonic-gate firstto->q_host); 14937c478bd9Sstevel@tonic-gate firstsig = firstto->q_signature; 14947c478bd9Sstevel@tonic-gate 14957c478bd9Sstevel@tonic-gate for (; to != NULL; to = to->q_next) 14967c478bd9Sstevel@tonic-gate { 14977c478bd9Sstevel@tonic-gate /* avoid sending multiple recipients to dumb mailers */ 14987c478bd9Sstevel@tonic-gate if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 14997c478bd9Sstevel@tonic-gate break; 15007c478bd9Sstevel@tonic-gate 15017c478bd9Sstevel@tonic-gate /* if already sent or not for this host, don't send */ 15027c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 15037c478bd9Sstevel@tonic-gate continue; 15047c478bd9Sstevel@tonic-gate 15057c478bd9Sstevel@tonic-gate /* 15067c478bd9Sstevel@tonic-gate ** Must be same mailer to keep grouping rcpts. 15077c478bd9Sstevel@tonic-gate ** If mailers don't match: continue; sendqueue is not 15087c478bd9Sstevel@tonic-gate ** sorted by mailers, so don't break; 15097c478bd9Sstevel@tonic-gate */ 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate if (to->q_mailer != firstto->q_mailer) 15127c478bd9Sstevel@tonic-gate continue; 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate if (to->q_signature == NULL) /* for safety */ 15157c478bd9Sstevel@tonic-gate to->q_signature = hostsignature(to->q_mailer, 15167c478bd9Sstevel@tonic-gate to->q_host); 15177c478bd9Sstevel@tonic-gate 15187c478bd9Sstevel@tonic-gate /* 15197c478bd9Sstevel@tonic-gate ** This is for coincidental and tailcoat piggybacking messages 15207c478bd9Sstevel@tonic-gate ** to the same mail host. While the signatures are identical 15217c478bd9Sstevel@tonic-gate ** (that's the MX-RR's are identical) we can do coincidental 15227c478bd9Sstevel@tonic-gate ** piggybacking. We try hard for coattail piggybacking 15237c478bd9Sstevel@tonic-gate ** with the same mail host when the next recipient has the 15247c478bd9Sstevel@tonic-gate ** same host at lowest preference. It may be that this 15257c478bd9Sstevel@tonic-gate ** won't work out, so 'skip_back' is maintained if a backup 15267c478bd9Sstevel@tonic-gate ** to coincidental piggybacking or full signature must happen. 15277c478bd9Sstevel@tonic-gate */ 15287c478bd9Sstevel@tonic-gate 15297c478bd9Sstevel@tonic-gate ret = firstto == to ? HS_MATCH_FULL : 15307c478bd9Sstevel@tonic-gate coloncmp(to->q_signature, firstsig); 15317c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_FULL) 15327c478bd9Sstevel@tonic-gate skip_back = to; 15337c478bd9Sstevel@tonic-gate else if (ret == HS_MATCH_NO) 15347c478bd9Sstevel@tonic-gate break; 15357c478bd9Sstevel@tonic-gate 15367c478bd9Sstevel@tonic-gate if (!clever) 15377c478bd9Sstevel@tonic-gate { 15387c478bd9Sstevel@tonic-gate /* avoid overflowing tobuf */ 15397c478bd9Sstevel@tonic-gate strsize += strlen(to->q_paddr) + 1; 15407c478bd9Sstevel@tonic-gate if (strsize > TOBUFSIZE) 15417c478bd9Sstevel@tonic-gate break; 15427c478bd9Sstevel@tonic-gate } 15437c478bd9Sstevel@tonic-gate 15447c478bd9Sstevel@tonic-gate if (++rcptcount > to->q_mailer->m_maxrcpt) 15457c478bd9Sstevel@tonic-gate break; 15467c478bd9Sstevel@tonic-gate 15477c478bd9Sstevel@tonic-gate if (tTd(10, 1)) 15487c478bd9Sstevel@tonic-gate { 15497c478bd9Sstevel@tonic-gate sm_dprintf("\nsend to "); 15507c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), to, false); 15517c478bd9Sstevel@tonic-gate } 15527c478bd9Sstevel@tonic-gate 15537c478bd9Sstevel@tonic-gate /* compute effective uid/gid when sending */ 15547c478bd9Sstevel@tonic-gate if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 15557c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT 15567c478bd9Sstevel@tonic-gate contextaddr = ctladdr = getctladdr(to); 15577c478bd9Sstevel@tonic-gate # else /* HASSETUSERCONTEXT */ 15587c478bd9Sstevel@tonic-gate ctladdr = getctladdr(to); 15597c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */ 15607c478bd9Sstevel@tonic-gate 15617c478bd9Sstevel@tonic-gate if (tTd(10, 2)) 15627c478bd9Sstevel@tonic-gate { 15637c478bd9Sstevel@tonic-gate sm_dprintf("ctladdr="); 15647c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ctladdr, false); 15657c478bd9Sstevel@tonic-gate } 15667c478bd9Sstevel@tonic-gate 15677c478bd9Sstevel@tonic-gate user = to->q_user; 15687c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 15697c478bd9Sstevel@tonic-gate 15707c478bd9Sstevel@tonic-gate /* 15717c478bd9Sstevel@tonic-gate ** Check to see that these people are allowed to 15727c478bd9Sstevel@tonic-gate ** talk to each other. 15737c478bd9Sstevel@tonic-gate ** Check also for overflow of e_msgsize. 15747c478bd9Sstevel@tonic-gate */ 15757c478bd9Sstevel@tonic-gate 15767c478bd9Sstevel@tonic-gate if (m->m_maxsize != 0 && 15777c478bd9Sstevel@tonic-gate (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 15787c478bd9Sstevel@tonic-gate { 15797c478bd9Sstevel@tonic-gate e->e_flags |= EF_NO_BODY_RETN; 15807c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 15817c478bd9Sstevel@tonic-gate to->q_status = "5.2.3"; 15827c478bd9Sstevel@tonic-gate else 15837c478bd9Sstevel@tonic-gate to->q_status = "5.3.4"; 15847c478bd9Sstevel@tonic-gate 15857c478bd9Sstevel@tonic-gate /* set to->q_rstatus = NULL; or to the following? */ 15867c478bd9Sstevel@tonic-gate usrerrenh(to->q_status, 15877c478bd9Sstevel@tonic-gate "552 Message is too large; %ld bytes max", 15887c478bd9Sstevel@tonic-gate m->m_maxsize); 15897c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, EX_UNAVAILABLE, false); 15907c478bd9Sstevel@tonic-gate giveresponse(EX_UNAVAILABLE, to->q_status, m, 15917c478bd9Sstevel@tonic-gate NULL, ctladdr, xstart, e, to); 15927c478bd9Sstevel@tonic-gate continue; 15937c478bd9Sstevel@tonic-gate } 15947c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 15957c478bd9Sstevel@tonic-gate ovr = true; 15967c478bd9Sstevel@tonic-gate 15977c478bd9Sstevel@tonic-gate /* do config file checking of compatibility */ 15987c478bd9Sstevel@tonic-gate quarantine = (e->e_quarmsg != NULL); 15997c478bd9Sstevel@tonic-gate rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 16007c478bd9Sstevel@tonic-gate e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1601*058561cbSjbeck e->e_id, NULL); 16027c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 16037c478bd9Sstevel@tonic-gate { 16047c478bd9Sstevel@tonic-gate /* do in-code checking if not discarding */ 16057c478bd9Sstevel@tonic-gate if (!bitset(EF_DISCARD, e->e_flags)) 16067c478bd9Sstevel@tonic-gate { 16077c478bd9Sstevel@tonic-gate rcode = checkcompat(to, e); 16087c478bd9Sstevel@tonic-gate ovr = false; 16097c478bd9Sstevel@tonic-gate } 16107c478bd9Sstevel@tonic-gate } 16117c478bd9Sstevel@tonic-gate if (rcode != EX_OK) 16127c478bd9Sstevel@tonic-gate { 16137c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, rcode, ovr); 16147c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, 16157c478bd9Sstevel@tonic-gate NULL, ctladdr, xstart, e, to); 16167c478bd9Sstevel@tonic-gate continue; 16177c478bd9Sstevel@tonic-gate } 16187c478bd9Sstevel@tonic-gate if (!quarantine && e->e_quarmsg != NULL) 16197c478bd9Sstevel@tonic-gate { 16207c478bd9Sstevel@tonic-gate /* 16217c478bd9Sstevel@tonic-gate ** check_compat or checkcompat() has tried 16227c478bd9Sstevel@tonic-gate ** to quarantine but that isn't supported. 16237c478bd9Sstevel@tonic-gate ** Revert the attempt. 16247c478bd9Sstevel@tonic-gate */ 16257c478bd9Sstevel@tonic-gate 16267c478bd9Sstevel@tonic-gate e->e_quarmsg = NULL; 16277c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 16287c478bd9Sstevel@tonic-gate macid("{quarantine}"), ""); 16297c478bd9Sstevel@tonic-gate } 16307c478bd9Sstevel@tonic-gate if (bitset(EF_DISCARD, e->e_flags)) 16317c478bd9Sstevel@tonic-gate { 16327c478bd9Sstevel@tonic-gate if (tTd(10, 5)) 16337c478bd9Sstevel@tonic-gate { 16347c478bd9Sstevel@tonic-gate sm_dprintf("deliver: discarding recipient "); 16357c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), to, false); 16367c478bd9Sstevel@tonic-gate } 16377c478bd9Sstevel@tonic-gate 16387c478bd9Sstevel@tonic-gate /* pretend the message was sent */ 16397c478bd9Sstevel@tonic-gate /* XXX should we log something here? */ 16407c478bd9Sstevel@tonic-gate to->q_state = QS_DISCARDED; 16417c478bd9Sstevel@tonic-gate 16427c478bd9Sstevel@tonic-gate /* 16437c478bd9Sstevel@tonic-gate ** Remove discard bit to prevent discard of 16447c478bd9Sstevel@tonic-gate ** future recipients. This is safe because the 16457c478bd9Sstevel@tonic-gate ** true "global discard" has been handled before 16467c478bd9Sstevel@tonic-gate ** we get here. 16477c478bd9Sstevel@tonic-gate */ 16487c478bd9Sstevel@tonic-gate 16497c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_DISCARD; 16507c478bd9Sstevel@tonic-gate continue; 16517c478bd9Sstevel@tonic-gate } 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate /* 16547c478bd9Sstevel@tonic-gate ** Strip quote bits from names if the mailer is dumb 16557c478bd9Sstevel@tonic-gate ** about them. 16567c478bd9Sstevel@tonic-gate */ 16577c478bd9Sstevel@tonic-gate 16587c478bd9Sstevel@tonic-gate if (bitnset(M_STRIPQ, m->m_flags)) 16597c478bd9Sstevel@tonic-gate { 16607c478bd9Sstevel@tonic-gate stripquotes(user); 16617c478bd9Sstevel@tonic-gate stripquotes(host); 16627c478bd9Sstevel@tonic-gate } 16637c478bd9Sstevel@tonic-gate 16647c478bd9Sstevel@tonic-gate /* 16657c478bd9Sstevel@tonic-gate ** Strip all leading backslashes if requested and the 16667c478bd9Sstevel@tonic-gate ** next character is alphanumerical (the latter can 16677c478bd9Sstevel@tonic-gate ** probably relaxed a bit, see RFC2821). 16687c478bd9Sstevel@tonic-gate */ 16697c478bd9Sstevel@tonic-gate 16707c478bd9Sstevel@tonic-gate if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 16717c478bd9Sstevel@tonic-gate stripbackslash(user); 16727c478bd9Sstevel@tonic-gate 16737c478bd9Sstevel@tonic-gate /* hack attack -- delivermail compatibility */ 16747c478bd9Sstevel@tonic-gate if (m == ProgMailer && *user == '|') 16757c478bd9Sstevel@tonic-gate user++; 16767c478bd9Sstevel@tonic-gate 16777c478bd9Sstevel@tonic-gate /* 16787c478bd9Sstevel@tonic-gate ** If an error message has already been given, don't 16797c478bd9Sstevel@tonic-gate ** bother to send to this address. 16807c478bd9Sstevel@tonic-gate ** 16817c478bd9Sstevel@tonic-gate ** >>>>>>>>>> This clause assumes that the local mailer 16827c478bd9Sstevel@tonic-gate ** >> NOTE >> cannot do any further aliasing; that 16837c478bd9Sstevel@tonic-gate ** >>>>>>>>>> function is subsumed by sendmail. 16847c478bd9Sstevel@tonic-gate */ 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 16877c478bd9Sstevel@tonic-gate continue; 16887c478bd9Sstevel@tonic-gate 16897c478bd9Sstevel@tonic-gate /* 16907c478bd9Sstevel@tonic-gate ** See if this user name is "special". 16917c478bd9Sstevel@tonic-gate ** If the user name has a slash in it, assume that this 16927c478bd9Sstevel@tonic-gate ** is a file -- send it off without further ado. Note 16937c478bd9Sstevel@tonic-gate ** that this type of addresses is not processed along 16947c478bd9Sstevel@tonic-gate ** with the others, so we fudge on the To person. 16957c478bd9Sstevel@tonic-gate */ 16967c478bd9Sstevel@tonic-gate 16977c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[FILE]") == 0) 16987c478bd9Sstevel@tonic-gate { 16997c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'u', user); 17007c478bd9Sstevel@tonic-gate p = to->q_home; 17017c478bd9Sstevel@tonic-gate if (p == NULL && ctladdr != NULL) 17027c478bd9Sstevel@tonic-gate p = ctladdr->q_home; 17037c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'z', p); 1704*058561cbSjbeck expand(m->m_argv[1], buf, sizeof(buf), e); 17057c478bd9Sstevel@tonic-gate if (strlen(buf) > 0) 17067c478bd9Sstevel@tonic-gate rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 17077c478bd9Sstevel@tonic-gate else 17087c478bd9Sstevel@tonic-gate { 17097c478bd9Sstevel@tonic-gate syserr("empty filename specification for mailer %s", 17107c478bd9Sstevel@tonic-gate m->m_name); 17117c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 17127c478bd9Sstevel@tonic-gate } 17137c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, NULL, 17147c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 17157c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, rcode, true); 17167c478bd9Sstevel@tonic-gate e->e_nsent++; 17177c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 17187c478bd9Sstevel@tonic-gate { 17197c478bd9Sstevel@tonic-gate to->q_state = QS_SENT; 17207c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 17217c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags)) 17227c478bd9Sstevel@tonic-gate { 17237c478bd9Sstevel@tonic-gate to->q_flags |= QDELIVERED; 17247c478bd9Sstevel@tonic-gate to->q_status = "2.1.5"; 17257c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, 17267c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 17277c478bd9Sstevel@tonic-gate "%s... Successfully delivered\n", 17287c478bd9Sstevel@tonic-gate to->q_paddr); 17297c478bd9Sstevel@tonic-gate } 17307c478bd9Sstevel@tonic-gate } 17317c478bd9Sstevel@tonic-gate to->q_statdate = curtime(); 17327c478bd9Sstevel@tonic-gate markstats(e, to, STATS_NORMAL); 17337c478bd9Sstevel@tonic-gate continue; 17347c478bd9Sstevel@tonic-gate } 17357c478bd9Sstevel@tonic-gate 17367c478bd9Sstevel@tonic-gate /* 17377c478bd9Sstevel@tonic-gate ** Address is verified -- add this user to mailer 17387c478bd9Sstevel@tonic-gate ** argv, and add it to the print list of recipients. 17397c478bd9Sstevel@tonic-gate */ 17407c478bd9Sstevel@tonic-gate 17417c478bd9Sstevel@tonic-gate /* link together the chain of recipients */ 17427c478bd9Sstevel@tonic-gate to->q_tchain = tochain; 17437c478bd9Sstevel@tonic-gate tochain = to; 17447c478bd9Sstevel@tonic-gate e->e_to = "[CHAIN]"; 17457c478bd9Sstevel@tonic-gate 17467c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 17477c478bd9Sstevel@tonic-gate p = to->q_home; 17487c478bd9Sstevel@tonic-gate if (p == NULL && ctladdr != NULL) 17497c478bd9Sstevel@tonic-gate p = ctladdr->q_home; 17507c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 17517c478bd9Sstevel@tonic-gate 17527c478bd9Sstevel@tonic-gate /* set the ${dsn_notify} macro if applicable */ 17537c478bd9Sstevel@tonic-gate if (bitset(QHASNOTIFY, to->q_flags)) 17547c478bd9Sstevel@tonic-gate { 17557c478bd9Sstevel@tonic-gate char notify[MAXLINE]; 17567c478bd9Sstevel@tonic-gate 17577c478bd9Sstevel@tonic-gate notify[0] = '\0'; 17587c478bd9Sstevel@tonic-gate if (bitset(QPINGONSUCCESS, to->q_flags)) 17597c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "SUCCESS,", 1760*058561cbSjbeck sizeof(notify)); 17617c478bd9Sstevel@tonic-gate if (bitset(QPINGONFAILURE, to->q_flags)) 17627c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "FAILURE,", 1763*058561cbSjbeck sizeof(notify)); 17647c478bd9Sstevel@tonic-gate if (bitset(QPINGONDELAY, to->q_flags)) 17657c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "DELAY,", 1766*058561cbSjbeck sizeof(notify)); 17677c478bd9Sstevel@tonic-gate 17687c478bd9Sstevel@tonic-gate /* Set to NEVER or drop trailing comma */ 17697c478bd9Sstevel@tonic-gate if (notify[0] == '\0') 17707c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "NEVER", 1771*058561cbSjbeck sizeof(notify)); 17727c478bd9Sstevel@tonic-gate else 17737c478bd9Sstevel@tonic-gate notify[strlen(notify) - 1] = '\0'; 17747c478bd9Sstevel@tonic-gate 17757c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_TEMP, 17767c478bd9Sstevel@tonic-gate macid("{dsn_notify}"), notify); 17777c478bd9Sstevel@tonic-gate } 17787c478bd9Sstevel@tonic-gate else 17797c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 17807c478bd9Sstevel@tonic-gate macid("{dsn_notify}"), NULL); 17817c478bd9Sstevel@tonic-gate 17827c478bd9Sstevel@tonic-gate /* 17837c478bd9Sstevel@tonic-gate ** Expand out this user into argument list. 17847c478bd9Sstevel@tonic-gate */ 17857c478bd9Sstevel@tonic-gate 17867c478bd9Sstevel@tonic-gate if (!clever) 17877c478bd9Sstevel@tonic-gate { 1788*058561cbSjbeck expand(*mvp, buf, sizeof(buf), e); 17897c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 17907c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV - 2]) 17917c478bd9Sstevel@tonic-gate { 17927c478bd9Sstevel@tonic-gate /* allow some space for trailing parms */ 17937c478bd9Sstevel@tonic-gate break; 17947c478bd9Sstevel@tonic-gate } 17957c478bd9Sstevel@tonic-gate } 17967c478bd9Sstevel@tonic-gate } 17977c478bd9Sstevel@tonic-gate 17987c478bd9Sstevel@tonic-gate /* see if any addresses still exist */ 17997c478bd9Sstevel@tonic-gate if (tochain == NULL) 18007c478bd9Sstevel@tonic-gate { 18017c478bd9Sstevel@tonic-gate rcode = 0; 18027c478bd9Sstevel@tonic-gate goto cleanup; 18037c478bd9Sstevel@tonic-gate } 18047c478bd9Sstevel@tonic-gate 18057c478bd9Sstevel@tonic-gate /* print out messages as full list */ 18067c478bd9Sstevel@tonic-gate strsize = 1; 18077c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 18087c478bd9Sstevel@tonic-gate strsize += strlen(to->q_paddr) + 1; 18097c478bd9Sstevel@tonic-gate if (strsize < TOBUFSIZE) 18107c478bd9Sstevel@tonic-gate strsize = TOBUFSIZE; 18117c478bd9Sstevel@tonic-gate if (strsize > tobufsize) 18127c478bd9Sstevel@tonic-gate { 18137c478bd9Sstevel@tonic-gate SM_FREE_CLR(tobuf); 18147c478bd9Sstevel@tonic-gate tobuf = sm_pmalloc_x(strsize); 18157c478bd9Sstevel@tonic-gate tobufsize = strsize; 18167c478bd9Sstevel@tonic-gate } 18177c478bd9Sstevel@tonic-gate p = tobuf; 18187c478bd9Sstevel@tonic-gate *p = '\0'; 18197c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 18207c478bd9Sstevel@tonic-gate { 18217c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 18227c478bd9Sstevel@tonic-gate ",", to->q_paddr); 18237c478bd9Sstevel@tonic-gate p += strlen(p); 18247c478bd9Sstevel@tonic-gate } 18257c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 18267c478bd9Sstevel@tonic-gate 18277c478bd9Sstevel@tonic-gate /* 18287c478bd9Sstevel@tonic-gate ** Fill out any parameters after the $u parameter. 18297c478bd9Sstevel@tonic-gate */ 18307c478bd9Sstevel@tonic-gate 18317c478bd9Sstevel@tonic-gate if (!clever) 18327c478bd9Sstevel@tonic-gate { 18337c478bd9Sstevel@tonic-gate while (*++mvp != NULL) 18347c478bd9Sstevel@tonic-gate { 1835*058561cbSjbeck expand(*mvp, buf, sizeof(buf), e); 18367c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 18377c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV]) 18387c478bd9Sstevel@tonic-gate syserr("554 5.3.0 deliver: pv overflow after $u for %s", 18397c478bd9Sstevel@tonic-gate pv[0]); 18407c478bd9Sstevel@tonic-gate } 18417c478bd9Sstevel@tonic-gate } 18427c478bd9Sstevel@tonic-gate *pvp++ = NULL; 18437c478bd9Sstevel@tonic-gate 18447c478bd9Sstevel@tonic-gate /* 18457c478bd9Sstevel@tonic-gate ** Call the mailer. 18467c478bd9Sstevel@tonic-gate ** The argument vector gets built, pipes 18477c478bd9Sstevel@tonic-gate ** are created as necessary, and we fork & exec as 18487c478bd9Sstevel@tonic-gate ** appropriate. 18497c478bd9Sstevel@tonic-gate ** If we are running SMTP, we just need to clean up. 18507c478bd9Sstevel@tonic-gate */ 18517c478bd9Sstevel@tonic-gate 18527c478bd9Sstevel@tonic-gate /* XXX this seems a bit wierd */ 18537c478bd9Sstevel@tonic-gate if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 18547c478bd9Sstevel@tonic-gate bitset(QGOODUID, e->e_from.q_flags)) 18557c478bd9Sstevel@tonic-gate ctladdr = &e->e_from; 18567c478bd9Sstevel@tonic-gate 18577c478bd9Sstevel@tonic-gate #if NAMED_BIND 18587c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 18597c478bd9Sstevel@tonic-gate _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 18607c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 18617c478bd9Sstevel@tonic-gate 18627c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 18637c478bd9Sstevel@tonic-gate { 18647c478bd9Sstevel@tonic-gate sm_dprintf("openmailer:"); 18657c478bd9Sstevel@tonic-gate printav(sm_debug_file(), pv); 18667c478bd9Sstevel@tonic-gate } 18677c478bd9Sstevel@tonic-gate errno = 0; 18687c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 18697c478bd9Sstevel@tonic-gate CurHostName = NULL; 18707c478bd9Sstevel@tonic-gate 18717c478bd9Sstevel@tonic-gate /* 18727c478bd9Sstevel@tonic-gate ** Deal with the special case of mail handled through an IPC 18737c478bd9Sstevel@tonic-gate ** connection. 18747c478bd9Sstevel@tonic-gate ** In this case we don't actually fork. We must be 18757c478bd9Sstevel@tonic-gate ** running SMTP for this to work. We will return a 18767c478bd9Sstevel@tonic-gate ** zero pid to indicate that we are running IPC. 18777c478bd9Sstevel@tonic-gate ** We also handle a debug version that just talks to stdin/out. 18787c478bd9Sstevel@tonic-gate */ 18797c478bd9Sstevel@tonic-gate 18807c478bd9Sstevel@tonic-gate curhost = NULL; 18817c478bd9Sstevel@tonic-gate SmtpPhase = NULL; 18827c478bd9Sstevel@tonic-gate mci = NULL; 18837c478bd9Sstevel@tonic-gate 18847c478bd9Sstevel@tonic-gate #if XDEBUG 18857c478bd9Sstevel@tonic-gate { 18867c478bd9Sstevel@tonic-gate char wbuf[MAXLINE]; 18877c478bd9Sstevel@tonic-gate 18887c478bd9Sstevel@tonic-gate /* make absolutely certain 0, 1, and 2 are in use */ 1889*058561cbSjbeck (void) sm_snprintf(wbuf, sizeof(wbuf), "%s... openmailer(%s)", 18907c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 18917c478bd9Sstevel@tonic-gate m->m_name); 18927c478bd9Sstevel@tonic-gate checkfd012(wbuf); 18937c478bd9Sstevel@tonic-gate } 18947c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 18957c478bd9Sstevel@tonic-gate 18967c478bd9Sstevel@tonic-gate /* check for 8-bit available */ 18977c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 18987c478bd9Sstevel@tonic-gate bitnset(M_7BITS, m->m_flags) && 18997c478bd9Sstevel@tonic-gate (bitset(EF_DONT_MIME, e->e_flags) || 19007c478bd9Sstevel@tonic-gate !(bitset(MM_MIME8BIT, MimeMode) || 19017c478bd9Sstevel@tonic-gate (bitset(EF_IS_MIME, e->e_flags) && 19027c478bd9Sstevel@tonic-gate bitset(MM_CVTMIME, MimeMode))))) 19037c478bd9Sstevel@tonic-gate { 19047c478bd9Sstevel@tonic-gate e->e_status = "5.6.3"; 19057c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 19067c478bd9Sstevel@tonic-gate "554 Cannot send 8-bit data to 7-bit destination"); 19077c478bd9Sstevel@tonic-gate rcode = EX_DATAERR; 19087c478bd9Sstevel@tonic-gate goto give_up; 19097c478bd9Sstevel@tonic-gate } 19107c478bd9Sstevel@tonic-gate 19117c478bd9Sstevel@tonic-gate if (tTd(62, 8)) 19127c478bd9Sstevel@tonic-gate checkfds("before delivery"); 19137c478bd9Sstevel@tonic-gate 19147c478bd9Sstevel@tonic-gate /* check for Local Person Communication -- not for mortals!!! */ 19157c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[LPC]") == 0) 19167c478bd9Sstevel@tonic-gate { 19177c478bd9Sstevel@tonic-gate if (clever) 19187c478bd9Sstevel@tonic-gate { 19197c478bd9Sstevel@tonic-gate /* flush any expired connections */ 19207c478bd9Sstevel@tonic-gate (void) mci_scan(NULL); 19217c478bd9Sstevel@tonic-gate 19227c478bd9Sstevel@tonic-gate /* try to get a cached connection or just a slot */ 19237c478bd9Sstevel@tonic-gate mci = mci_get(m->m_name, m); 19247c478bd9Sstevel@tonic-gate if (mci->mci_host == NULL) 19257c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 19267c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 19277c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 19287c478bd9Sstevel@tonic-gate { 19297c478bd9Sstevel@tonic-gate message("Using cached SMTP/LPC connection for %s...", 19307c478bd9Sstevel@tonic-gate m->m_name); 19317c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 19327c478bd9Sstevel@tonic-gate goto do_transfer; 19337c478bd9Sstevel@tonic-gate } 19347c478bd9Sstevel@tonic-gate } 19357c478bd9Sstevel@tonic-gate else 19367c478bd9Sstevel@tonic-gate { 19377c478bd9Sstevel@tonic-gate mci = mci_new(e->e_rpool); 19387c478bd9Sstevel@tonic-gate } 19397c478bd9Sstevel@tonic-gate mci->mci_in = smioin; 19407c478bd9Sstevel@tonic-gate mci->mci_out = smioout; 19417c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 19427c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 19437c478bd9Sstevel@tonic-gate if (clever) 19447c478bd9Sstevel@tonic-gate { 19457c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 19467c478bd9Sstevel@tonic-gate mci_cache(mci); 19477c478bd9Sstevel@tonic-gate } 19487c478bd9Sstevel@tonic-gate else 19497c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 19507c478bd9Sstevel@tonic-gate } 19517c478bd9Sstevel@tonic-gate else if (strcmp(m->m_mailer, "[IPC]") == 0) 19527c478bd9Sstevel@tonic-gate { 19537c478bd9Sstevel@tonic-gate register int i; 19547c478bd9Sstevel@tonic-gate 19557c478bd9Sstevel@tonic-gate if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 19567c478bd9Sstevel@tonic-gate { 19577c478bd9Sstevel@tonic-gate syserr("null destination for %s mailer", m->m_mailer); 19587c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 19597c478bd9Sstevel@tonic-gate goto give_up; 19607c478bd9Sstevel@tonic-gate } 19617c478bd9Sstevel@tonic-gate 19627c478bd9Sstevel@tonic-gate # if NETUNIX 19637c478bd9Sstevel@tonic-gate if (strcmp(pv[0], "FILE") == 0) 19647c478bd9Sstevel@tonic-gate { 19657c478bd9Sstevel@tonic-gate curhost = CurHostName = "localhost"; 19667c478bd9Sstevel@tonic-gate mux_path = pv[1]; 19677c478bd9Sstevel@tonic-gate } 19687c478bd9Sstevel@tonic-gate else 19697c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 19707c478bd9Sstevel@tonic-gate { 19717c478bd9Sstevel@tonic-gate CurHostName = pv[1]; 19727c478bd9Sstevel@tonic-gate curhost = hostsignature(m, pv[1]); 19737c478bd9Sstevel@tonic-gate } 19747c478bd9Sstevel@tonic-gate 19757c478bd9Sstevel@tonic-gate if (curhost == NULL || curhost[0] == '\0') 19767c478bd9Sstevel@tonic-gate { 19777c478bd9Sstevel@tonic-gate syserr("null host signature for %s", pv[1]); 19787c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 19797c478bd9Sstevel@tonic-gate goto give_up; 19807c478bd9Sstevel@tonic-gate } 19817c478bd9Sstevel@tonic-gate 19827c478bd9Sstevel@tonic-gate if (!clever) 19837c478bd9Sstevel@tonic-gate { 19847c478bd9Sstevel@tonic-gate syserr("554 5.3.5 non-clever IPC"); 19857c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 19867c478bd9Sstevel@tonic-gate goto give_up; 19877c478bd9Sstevel@tonic-gate } 19887c478bd9Sstevel@tonic-gate if (pv[2] != NULL 19897c478bd9Sstevel@tonic-gate # if NETUNIX 19907c478bd9Sstevel@tonic-gate && mux_path == NULL 19917c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 19927c478bd9Sstevel@tonic-gate ) 19937c478bd9Sstevel@tonic-gate { 19947c478bd9Sstevel@tonic-gate port = htons((unsigned short) atoi(pv[2])); 19957c478bd9Sstevel@tonic-gate if (port == 0) 19967c478bd9Sstevel@tonic-gate { 19977c478bd9Sstevel@tonic-gate # ifdef NO_GETSERVBYNAME 19987c478bd9Sstevel@tonic-gate syserr("Invalid port number: %s", pv[2]); 19997c478bd9Sstevel@tonic-gate # else /* NO_GETSERVBYNAME */ 20007c478bd9Sstevel@tonic-gate struct servent *sp = getservbyname(pv[2], "tcp"); 20017c478bd9Sstevel@tonic-gate 20027c478bd9Sstevel@tonic-gate if (sp == NULL) 20037c478bd9Sstevel@tonic-gate syserr("Service %s unknown", pv[2]); 20047c478bd9Sstevel@tonic-gate else 20057c478bd9Sstevel@tonic-gate port = sp->s_port; 20067c478bd9Sstevel@tonic-gate # endif /* NO_GETSERVBYNAME */ 20077c478bd9Sstevel@tonic-gate } 20087c478bd9Sstevel@tonic-gate } 20097c478bd9Sstevel@tonic-gate 20107c478bd9Sstevel@tonic-gate nummxhosts = parse_hostsignature(curhost, mxhosts, m); 20117c478bd9Sstevel@tonic-gate if (TimeOuts.to_aconnect > 0) 20127c478bd9Sstevel@tonic-gate enough = curtime() + TimeOuts.to_aconnect; 20137c478bd9Sstevel@tonic-gate tryhost: 20147c478bd9Sstevel@tonic-gate while (hostnum < nummxhosts) 20157c478bd9Sstevel@tonic-gate { 20167c478bd9Sstevel@tonic-gate char sep = ':'; 20177c478bd9Sstevel@tonic-gate char *endp; 20187c478bd9Sstevel@tonic-gate static char hostbuf[MAXNAME + 1]; 20197c478bd9Sstevel@tonic-gate bool tried_fallbacksmarthost = false; 20207c478bd9Sstevel@tonic-gate 20217c478bd9Sstevel@tonic-gate # if NETINET6 20227c478bd9Sstevel@tonic-gate if (*mxhosts[hostnum] == '[') 20237c478bd9Sstevel@tonic-gate { 20247c478bd9Sstevel@tonic-gate endp = strchr(mxhosts[hostnum] + 1, ']'); 20257c478bd9Sstevel@tonic-gate if (endp != NULL) 20267c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 20277c478bd9Sstevel@tonic-gate } 20287c478bd9Sstevel@tonic-gate else 20297c478bd9Sstevel@tonic-gate endp = strpbrk(mxhosts[hostnum], ":,"); 20307c478bd9Sstevel@tonic-gate # else /* NETINET6 */ 20317c478bd9Sstevel@tonic-gate endp = strpbrk(mxhosts[hostnum], ":,"); 20327c478bd9Sstevel@tonic-gate # endif /* NETINET6 */ 20337c478bd9Sstevel@tonic-gate if (endp != NULL) 20347c478bd9Sstevel@tonic-gate { 20357c478bd9Sstevel@tonic-gate sep = *endp; 20367c478bd9Sstevel@tonic-gate *endp = '\0'; 20377c478bd9Sstevel@tonic-gate } 20387c478bd9Sstevel@tonic-gate 20397c478bd9Sstevel@tonic-gate if (hostnum == 1 && skip_back != NULL) 20407c478bd9Sstevel@tonic-gate { 20417c478bd9Sstevel@tonic-gate /* 20427c478bd9Sstevel@tonic-gate ** Coattail piggybacking is no longer an 20437c478bd9Sstevel@tonic-gate ** option with the mail host next to be tried 20447c478bd9Sstevel@tonic-gate ** no longer the lowest MX preference 20457c478bd9Sstevel@tonic-gate ** (hostnum == 1 meaning we're on the second 20467c478bd9Sstevel@tonic-gate ** preference). We do not try to coattail 20477c478bd9Sstevel@tonic-gate ** piggyback more than the first MX preference. 20487c478bd9Sstevel@tonic-gate ** Revert 'tochain' to last location for 20497c478bd9Sstevel@tonic-gate ** coincidental piggybacking. This works this 20507c478bd9Sstevel@tonic-gate ** easily because the q_tchain kept getting 20517c478bd9Sstevel@tonic-gate ** added to the top of the linked list. 20527c478bd9Sstevel@tonic-gate */ 20537c478bd9Sstevel@tonic-gate 20547c478bd9Sstevel@tonic-gate tochain = skip_back; 20557c478bd9Sstevel@tonic-gate } 20567c478bd9Sstevel@tonic-gate 20577c478bd9Sstevel@tonic-gate if (*mxhosts[hostnum] == '\0') 20587c478bd9Sstevel@tonic-gate { 20597c478bd9Sstevel@tonic-gate syserr("deliver: null host name in signature"); 20607c478bd9Sstevel@tonic-gate hostnum++; 20617c478bd9Sstevel@tonic-gate if (endp != NULL) 20627c478bd9Sstevel@tonic-gate *endp = sep; 20637c478bd9Sstevel@tonic-gate continue; 20647c478bd9Sstevel@tonic-gate } 20657c478bd9Sstevel@tonic-gate (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 2066*058561cbSjbeck sizeof(hostbuf)); 20677c478bd9Sstevel@tonic-gate hostnum++; 20687c478bd9Sstevel@tonic-gate if (endp != NULL) 20697c478bd9Sstevel@tonic-gate *endp = sep; 20707c478bd9Sstevel@tonic-gate 20717c478bd9Sstevel@tonic-gate one_last_try: 20727c478bd9Sstevel@tonic-gate /* see if we already know that this host is fried */ 20737c478bd9Sstevel@tonic-gate CurHostName = hostbuf; 20747c478bd9Sstevel@tonic-gate mci = mci_get(hostbuf, m); 20757c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 20767c478bd9Sstevel@tonic-gate { 20777c478bd9Sstevel@tonic-gate char *type; 20787c478bd9Sstevel@tonic-gate 20797c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 20807c478bd9Sstevel@tonic-gate { 20817c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: "); 20827c478bd9Sstevel@tonic-gate mci_dump(sm_debug_file(), mci, false); 20837c478bd9Sstevel@tonic-gate } 20847c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 20857c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 20867c478bd9Sstevel@tonic-gate type = "L"; 20877c478bd9Sstevel@tonic-gate else if (bitset(MCIF_ESMTP, mci->mci_flags)) 20887c478bd9Sstevel@tonic-gate type = "ES"; 20897c478bd9Sstevel@tonic-gate else 20907c478bd9Sstevel@tonic-gate type = "S"; 20917c478bd9Sstevel@tonic-gate message("Using cached %sMTP connection to %s via %s...", 20927c478bd9Sstevel@tonic-gate type, hostbuf, m->m_name); 20937c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 20947c478bd9Sstevel@tonic-gate break; 20957c478bd9Sstevel@tonic-gate } 20967c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 20977c478bd9Sstevel@tonic-gate if (mci->mci_exitstat != EX_OK) 20987c478bd9Sstevel@tonic-gate { 20997c478bd9Sstevel@tonic-gate if (mci->mci_exitstat == EX_TEMPFAIL) 21007c478bd9Sstevel@tonic-gate goodmxfound = true; 21017c478bd9Sstevel@tonic-gate 21027c478bd9Sstevel@tonic-gate /* Try FallbackSmartHost? */ 21037c478bd9Sstevel@tonic-gate if (should_try_fbsh(e, &tried_fallbacksmarthost, 2104*058561cbSjbeck hostbuf, sizeof(hostbuf), 21057c478bd9Sstevel@tonic-gate mci->mci_exitstat)) 21067c478bd9Sstevel@tonic-gate goto one_last_try; 21077c478bd9Sstevel@tonic-gate 21087c478bd9Sstevel@tonic-gate continue; 21097c478bd9Sstevel@tonic-gate } 21107c478bd9Sstevel@tonic-gate 21117c478bd9Sstevel@tonic-gate if (mci_lock_host(mci) != EX_OK) 21127c478bd9Sstevel@tonic-gate { 21137c478bd9Sstevel@tonic-gate mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 21147c478bd9Sstevel@tonic-gate goodmxfound = true; 21157c478bd9Sstevel@tonic-gate continue; 21167c478bd9Sstevel@tonic-gate } 21177c478bd9Sstevel@tonic-gate 21187c478bd9Sstevel@tonic-gate /* try the connection */ 21197c478bd9Sstevel@tonic-gate sm_setproctitle(true, e, "%s %s: %s", 21207c478bd9Sstevel@tonic-gate qid_printname(e), 21217c478bd9Sstevel@tonic-gate hostbuf, "user open"); 21227c478bd9Sstevel@tonic-gate # if NETUNIX 21237c478bd9Sstevel@tonic-gate if (mux_path != NULL) 21247c478bd9Sstevel@tonic-gate { 21257c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", 21267c478bd9Sstevel@tonic-gate mux_path, m->m_name); 21277c478bd9Sstevel@tonic-gate i = makeconnection_ds((char *) mux_path, mci); 21287c478bd9Sstevel@tonic-gate } 21297c478bd9Sstevel@tonic-gate else 21307c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 21317c478bd9Sstevel@tonic-gate { 21327c478bd9Sstevel@tonic-gate if (port == 0) 21337c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", 21347c478bd9Sstevel@tonic-gate hostbuf, m->m_name); 21357c478bd9Sstevel@tonic-gate else 21367c478bd9Sstevel@tonic-gate message("Connecting to %s port %d via %s...", 21377c478bd9Sstevel@tonic-gate hostbuf, ntohs(port), 21387c478bd9Sstevel@tonic-gate m->m_name); 21397c478bd9Sstevel@tonic-gate i = makeconnection(hostbuf, port, mci, e, 21407c478bd9Sstevel@tonic-gate enough); 21417c478bd9Sstevel@tonic-gate } 21427c478bd9Sstevel@tonic-gate mci->mci_errno = errno; 21437c478bd9Sstevel@tonic-gate mci->mci_lastuse = curtime(); 21447c478bd9Sstevel@tonic-gate mci->mci_deliveries = 0; 21457c478bd9Sstevel@tonic-gate mci->mci_exitstat = i; 21467c478bd9Sstevel@tonic-gate # if NAMED_BIND 21477c478bd9Sstevel@tonic-gate mci->mci_herrno = h_errno; 21487c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */ 21497c478bd9Sstevel@tonic-gate 21507c478bd9Sstevel@tonic-gate /* 21517c478bd9Sstevel@tonic-gate ** Have we tried long enough to get a connection? 21527c478bd9Sstevel@tonic-gate ** If yes, skip to the fallback MX hosts 21537c478bd9Sstevel@tonic-gate ** (if existent). 21547c478bd9Sstevel@tonic-gate */ 21557c478bd9Sstevel@tonic-gate 21567c478bd9Sstevel@tonic-gate if (enough > 0 && mci->mci_lastuse >= enough) 21577c478bd9Sstevel@tonic-gate { 21587c478bd9Sstevel@tonic-gate int h; 21597c478bd9Sstevel@tonic-gate # if NAMED_BIND 21607c478bd9Sstevel@tonic-gate extern int NumFallbackMXHosts; 21617c478bd9Sstevel@tonic-gate # else /* NAMED_BIND */ 21627c478bd9Sstevel@tonic-gate const int NumFallbackMXHosts = 0; 21637c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */ 21647c478bd9Sstevel@tonic-gate 21657c478bd9Sstevel@tonic-gate if (hostnum < nummxhosts && LogLevel > 9) 21667c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 21677c478bd9Sstevel@tonic-gate "Timeout.to_aconnect occurred before exhausting all addresses"); 21687c478bd9Sstevel@tonic-gate 21697c478bd9Sstevel@tonic-gate /* turn off timeout if fallback available */ 21707c478bd9Sstevel@tonic-gate if (NumFallbackMXHosts > 0) 21717c478bd9Sstevel@tonic-gate enough = 0; 21727c478bd9Sstevel@tonic-gate 21737c478bd9Sstevel@tonic-gate /* skip to a fallback MX host */ 21747c478bd9Sstevel@tonic-gate h = nummxhosts - NumFallbackMXHosts; 21757c478bd9Sstevel@tonic-gate if (hostnum < h) 21767c478bd9Sstevel@tonic-gate hostnum = h; 21777c478bd9Sstevel@tonic-gate } 21787c478bd9Sstevel@tonic-gate if (i == EX_OK) 21797c478bd9Sstevel@tonic-gate { 21807c478bd9Sstevel@tonic-gate goodmxfound = true; 21817c478bd9Sstevel@tonic-gate markstats(e, firstto, STATS_CONNECT); 21827c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 21837c478bd9Sstevel@tonic-gate mci_cache(mci); 21847c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 21857c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 21867c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 21877c478bd9Sstevel@tonic-gate "%05d === CONNECT %s\n", 21887c478bd9Sstevel@tonic-gate (int) CurrentPid, 21897c478bd9Sstevel@tonic-gate hostbuf); 21907c478bd9Sstevel@tonic-gate break; 21917c478bd9Sstevel@tonic-gate } 21927c478bd9Sstevel@tonic-gate else 21937c478bd9Sstevel@tonic-gate { 21947c478bd9Sstevel@tonic-gate /* Try FallbackSmartHost? */ 21957c478bd9Sstevel@tonic-gate if (should_try_fbsh(e, &tried_fallbacksmarthost, 2196*058561cbSjbeck hostbuf, sizeof(hostbuf), i)) 21977c478bd9Sstevel@tonic-gate goto one_last_try; 21987c478bd9Sstevel@tonic-gate 21997c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 22007c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 22017c478bd9Sstevel@tonic-gate i, errno); 22027c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 22037c478bd9Sstevel@tonic-gate goodmxfound = true; 22047c478bd9Sstevel@tonic-gate mci_unlock_host(mci); 22057c478bd9Sstevel@tonic-gate } 22067c478bd9Sstevel@tonic-gate 22077c478bd9Sstevel@tonic-gate /* enter status of this host */ 22087c478bd9Sstevel@tonic-gate setstat(i); 22097c478bd9Sstevel@tonic-gate 22107c478bd9Sstevel@tonic-gate /* should print some message here for -v mode */ 22117c478bd9Sstevel@tonic-gate } 22127c478bd9Sstevel@tonic-gate if (mci == NULL) 22137c478bd9Sstevel@tonic-gate { 22147c478bd9Sstevel@tonic-gate syserr("deliver: no host name"); 22157c478bd9Sstevel@tonic-gate rcode = EX_SOFTWARE; 22167c478bd9Sstevel@tonic-gate goto give_up; 22177c478bd9Sstevel@tonic-gate } 22187c478bd9Sstevel@tonic-gate mci->mci_pid = 0; 22197c478bd9Sstevel@tonic-gate } 22207c478bd9Sstevel@tonic-gate else 22217c478bd9Sstevel@tonic-gate { 22227c478bd9Sstevel@tonic-gate /* flush any expired connections */ 22237c478bd9Sstevel@tonic-gate (void) mci_scan(NULL); 22247c478bd9Sstevel@tonic-gate mci = NULL; 22257c478bd9Sstevel@tonic-gate 22267c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 22277c478bd9Sstevel@tonic-gate { 22287c478bd9Sstevel@tonic-gate /* try to get a cached connection */ 22297c478bd9Sstevel@tonic-gate mci = mci_get(m->m_name, m); 22307c478bd9Sstevel@tonic-gate if (mci->mci_host == NULL) 22317c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 22327c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 22337c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 22347c478bd9Sstevel@tonic-gate { 22357c478bd9Sstevel@tonic-gate message("Using cached LMTP connection for %s...", 22367c478bd9Sstevel@tonic-gate m->m_name); 22377c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 22387c478bd9Sstevel@tonic-gate goto do_transfer; 22397c478bd9Sstevel@tonic-gate } 22407c478bd9Sstevel@tonic-gate } 22417c478bd9Sstevel@tonic-gate 22427c478bd9Sstevel@tonic-gate /* announce the connection to verbose listeners */ 22437c478bd9Sstevel@tonic-gate if (host == NULL || host[0] == '\0') 22447c478bd9Sstevel@tonic-gate message("Connecting to %s...", m->m_name); 22457c478bd9Sstevel@tonic-gate else 22467c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", host, m->m_name); 22477c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 22487c478bd9Sstevel@tonic-gate { 22497c478bd9Sstevel@tonic-gate char **av; 22507c478bd9Sstevel@tonic-gate 22517c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 22527c478bd9Sstevel@tonic-gate "%05d === EXEC", (int) CurrentPid); 22537c478bd9Sstevel@tonic-gate for (av = pv; *av != NULL; av++) 22547c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 22557c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, " %s", 22567c478bd9Sstevel@tonic-gate *av); 22577c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 22587c478bd9Sstevel@tonic-gate "\n"); 22597c478bd9Sstevel@tonic-gate } 22607c478bd9Sstevel@tonic-gate 22617c478bd9Sstevel@tonic-gate #if XDEBUG 22627c478bd9Sstevel@tonic-gate checkfd012("before creating mail pipe"); 22637c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 22647c478bd9Sstevel@tonic-gate 22657c478bd9Sstevel@tonic-gate /* create a pipe to shove the mail through */ 22667c478bd9Sstevel@tonic-gate if (pipe(mpvect) < 0) 22677c478bd9Sstevel@tonic-gate { 22687c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): pipe (to mailer)", 22697c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 22707c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 22717c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 22727c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 22737c478bd9Sstevel@tonic-gate goto give_up; 22747c478bd9Sstevel@tonic-gate } 22757c478bd9Sstevel@tonic-gate 22767c478bd9Sstevel@tonic-gate #if XDEBUG 22777c478bd9Sstevel@tonic-gate /* make sure we didn't get one of the standard I/O files */ 22787c478bd9Sstevel@tonic-gate if (mpvect[0] < 3 || mpvect[1] < 3) 22797c478bd9Sstevel@tonic-gate { 22807c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): bogus mpvect %d %d", 22817c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 22827c478bd9Sstevel@tonic-gate mpvect[0], mpvect[1]); 22837c478bd9Sstevel@tonic-gate printopenfds(true); 22847c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 22857c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 22867c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 22877c478bd9Sstevel@tonic-gate goto give_up; 22887c478bd9Sstevel@tonic-gate } 22897c478bd9Sstevel@tonic-gate 22907c478bd9Sstevel@tonic-gate /* make sure system call isn't dead meat */ 22917c478bd9Sstevel@tonic-gate checkfdopen(mpvect[0], "mpvect[0]"); 22927c478bd9Sstevel@tonic-gate checkfdopen(mpvect[1], "mpvect[1]"); 22937c478bd9Sstevel@tonic-gate if (mpvect[0] == mpvect[1] || 22947c478bd9Sstevel@tonic-gate (e->e_lockfp != NULL && 22957c478bd9Sstevel@tonic-gate (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 22967c478bd9Sstevel@tonic-gate NULL) || 22977c478bd9Sstevel@tonic-gate mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 22987c478bd9Sstevel@tonic-gate NULL)))) 22997c478bd9Sstevel@tonic-gate { 23007c478bd9Sstevel@tonic-gate if (e->e_lockfp == NULL) 23017c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): overlapping mpvect %d %d", 23027c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 23037c478bd9Sstevel@tonic-gate m->m_name, mpvect[0], mpvect[1]); 23047c478bd9Sstevel@tonic-gate else 23057c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 23067c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 23077c478bd9Sstevel@tonic-gate m->m_name, mpvect[0], mpvect[1], 23087c478bd9Sstevel@tonic-gate sm_io_getinfo(e->e_lockfp, 23097c478bd9Sstevel@tonic-gate SM_IO_WHAT_FD, NULL)); 23107c478bd9Sstevel@tonic-gate } 23117c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 23127c478bd9Sstevel@tonic-gate 23137c478bd9Sstevel@tonic-gate /* create a return pipe */ 23147c478bd9Sstevel@tonic-gate if (pipe(rpvect) < 0) 23157c478bd9Sstevel@tonic-gate { 23167c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): pipe (from mailer)", 23177c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 23187c478bd9Sstevel@tonic-gate m->m_name); 23197c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 23207c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 23217c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 23227c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 23237c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 23247c478bd9Sstevel@tonic-gate goto give_up; 23257c478bd9Sstevel@tonic-gate } 23267c478bd9Sstevel@tonic-gate #if XDEBUG 23277c478bd9Sstevel@tonic-gate checkfdopen(rpvect[0], "rpvect[0]"); 23287c478bd9Sstevel@tonic-gate checkfdopen(rpvect[1], "rpvect[1]"); 23297c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 23307c478bd9Sstevel@tonic-gate 23317c478bd9Sstevel@tonic-gate /* 23327c478bd9Sstevel@tonic-gate ** Actually fork the mailer process. 23337c478bd9Sstevel@tonic-gate ** DOFORK is clever about retrying. 23347c478bd9Sstevel@tonic-gate ** 23357c478bd9Sstevel@tonic-gate ** Dispose of SIGCHLD signal catchers that may be laying 23367c478bd9Sstevel@tonic-gate ** around so that endmailer will get it. 23377c478bd9Sstevel@tonic-gate */ 23387c478bd9Sstevel@tonic-gate 23397c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) /* for debugging */ 23407c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 23417c478bd9Sstevel@tonic-gate (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 23427c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 23437c478bd9Sstevel@tonic-gate 23447c478bd9Sstevel@tonic-gate 23457c478bd9Sstevel@tonic-gate DOFORK(FORK); 23467c478bd9Sstevel@tonic-gate /* pid is set by DOFORK */ 23477c478bd9Sstevel@tonic-gate 23487c478bd9Sstevel@tonic-gate if (pid < 0) 23497c478bd9Sstevel@tonic-gate { 23507c478bd9Sstevel@tonic-gate /* failure */ 23517c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot fork", 23527c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 23537c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 23547c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 23557c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 23567c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 23577c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 23587c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 23597c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 23607c478bd9Sstevel@tonic-gate goto give_up; 23617c478bd9Sstevel@tonic-gate } 23627c478bd9Sstevel@tonic-gate else if (pid == 0) 23637c478bd9Sstevel@tonic-gate { 23647c478bd9Sstevel@tonic-gate int save_errno; 23657c478bd9Sstevel@tonic-gate int sff; 23667c478bd9Sstevel@tonic-gate int new_euid = NO_UID; 23677c478bd9Sstevel@tonic-gate int new_ruid = NO_UID; 23687c478bd9Sstevel@tonic-gate int new_gid = NO_GID; 23697c478bd9Sstevel@tonic-gate char *user = NULL; 23707c478bd9Sstevel@tonic-gate struct stat stb; 23717c478bd9Sstevel@tonic-gate extern int DtableSize; 23727c478bd9Sstevel@tonic-gate 23737c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 23747c478bd9Sstevel@tonic-gate 23757c478bd9Sstevel@tonic-gate /* clear the events to turn off SIGALRMs */ 23767c478bd9Sstevel@tonic-gate sm_clear_events(); 23777c478bd9Sstevel@tonic-gate 23787c478bd9Sstevel@tonic-gate /* Reset global flags */ 23797c478bd9Sstevel@tonic-gate RestartRequest = NULL; 23807c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 23817c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 23827c478bd9Sstevel@tonic-gate PendingSignal = 0; 23837c478bd9Sstevel@tonic-gate 23847c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 23857c478bd9Sstevel@tonic-gate (void) close(sm_io_getinfo(e->e_lockfp, 23867c478bd9Sstevel@tonic-gate SM_IO_WHAT_FD, 23877c478bd9Sstevel@tonic-gate NULL)); 23887c478bd9Sstevel@tonic-gate 23897c478bd9Sstevel@tonic-gate /* child -- set up input & exec mailer */ 23907c478bd9Sstevel@tonic-gate (void) sm_signal(SIGALRM, sm_signal_noop); 23917c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 23927c478bd9Sstevel@tonic-gate (void) sm_signal(SIGHUP, SIG_IGN); 23937c478bd9Sstevel@tonic-gate (void) sm_signal(SIGINT, SIG_IGN); 23947c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 23957c478bd9Sstevel@tonic-gate # ifdef SIGUSR1 23967c478bd9Sstevel@tonic-gate (void) sm_signal(SIGUSR1, sm_signal_noop); 23977c478bd9Sstevel@tonic-gate # endif /* SIGUSR1 */ 23987c478bd9Sstevel@tonic-gate 23997c478bd9Sstevel@tonic-gate if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 24007c478bd9Sstevel@tonic-gate stb.st_mode = 0; 24017c478bd9Sstevel@tonic-gate 24027c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT 24037c478bd9Sstevel@tonic-gate /* 24047c478bd9Sstevel@tonic-gate ** Set user resources. 24057c478bd9Sstevel@tonic-gate */ 24067c478bd9Sstevel@tonic-gate 24077c478bd9Sstevel@tonic-gate if (contextaddr != NULL) 24087c478bd9Sstevel@tonic-gate { 24097c478bd9Sstevel@tonic-gate int sucflags; 24107c478bd9Sstevel@tonic-gate struct passwd *pwd; 24117c478bd9Sstevel@tonic-gate 24127c478bd9Sstevel@tonic-gate if (contextaddr->q_ruser != NULL) 24137c478bd9Sstevel@tonic-gate pwd = sm_getpwnam(contextaddr->q_ruser); 24147c478bd9Sstevel@tonic-gate else 24157c478bd9Sstevel@tonic-gate pwd = sm_getpwnam(contextaddr->q_user); 24167c478bd9Sstevel@tonic-gate sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 24177c478bd9Sstevel@tonic-gate #ifdef LOGIN_SETMAC 24187c478bd9Sstevel@tonic-gate sucflags |= LOGIN_SETMAC; 24197c478bd9Sstevel@tonic-gate #endif /* LOGIN_SETMAC */ 24207c478bd9Sstevel@tonic-gate if (pwd != NULL && 24217c478bd9Sstevel@tonic-gate setusercontext(NULL, pwd, pwd->pw_uid, 24227c478bd9Sstevel@tonic-gate sucflags) == -1 && 24237c478bd9Sstevel@tonic-gate suidwarn) 24247c478bd9Sstevel@tonic-gate { 24257c478bd9Sstevel@tonic-gate syserr("openmailer: setusercontext() failed"); 24267c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 24277c478bd9Sstevel@tonic-gate } 24287c478bd9Sstevel@tonic-gate } 24297c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */ 24307c478bd9Sstevel@tonic-gate 24317c478bd9Sstevel@tonic-gate #if HASNICE 24327c478bd9Sstevel@tonic-gate /* tweak niceness */ 24337c478bd9Sstevel@tonic-gate if (m->m_nice != 0) 24347c478bd9Sstevel@tonic-gate (void) nice(m->m_nice); 24357c478bd9Sstevel@tonic-gate #endif /* HASNICE */ 24367c478bd9Sstevel@tonic-gate 24377c478bd9Sstevel@tonic-gate /* reset group id */ 24387c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, m->m_flags)) 24397c478bd9Sstevel@tonic-gate { 24407c478bd9Sstevel@tonic-gate if (m->m_gid == NO_GID) 24417c478bd9Sstevel@tonic-gate new_gid = RunAsGid; 24427c478bd9Sstevel@tonic-gate else 24437c478bd9Sstevel@tonic-gate new_gid = m->m_gid; 24447c478bd9Sstevel@tonic-gate } 24457c478bd9Sstevel@tonic-gate else if (bitset(S_ISGID, stb.st_mode)) 24467c478bd9Sstevel@tonic-gate new_gid = stb.st_gid; 24477c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_gid != 0) 24487c478bd9Sstevel@tonic-gate { 24497c478bd9Sstevel@tonic-gate if (!DontInitGroups) 24507c478bd9Sstevel@tonic-gate { 24517c478bd9Sstevel@tonic-gate user = ctladdr->q_ruser; 24527c478bd9Sstevel@tonic-gate if (user == NULL) 24537c478bd9Sstevel@tonic-gate user = ctladdr->q_user; 24547c478bd9Sstevel@tonic-gate 24557c478bd9Sstevel@tonic-gate if (initgroups(user, 24567c478bd9Sstevel@tonic-gate ctladdr->q_gid) == -1 24577c478bd9Sstevel@tonic-gate && suidwarn) 24587c478bd9Sstevel@tonic-gate { 24597c478bd9Sstevel@tonic-gate syserr("openmailer: initgroups(%s, %d) failed", 24607c478bd9Sstevel@tonic-gate user, ctladdr->q_gid); 24617c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 24627c478bd9Sstevel@tonic-gate } 24637c478bd9Sstevel@tonic-gate } 24647c478bd9Sstevel@tonic-gate else 24657c478bd9Sstevel@tonic-gate { 24667c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 24677c478bd9Sstevel@tonic-gate 24687c478bd9Sstevel@tonic-gate gidset[0] = ctladdr->q_gid; 24697c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 24707c478bd9Sstevel@tonic-gate && suidwarn) 24717c478bd9Sstevel@tonic-gate { 24727c478bd9Sstevel@tonic-gate syserr("openmailer: setgroups() failed"); 24737c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 24747c478bd9Sstevel@tonic-gate } 24757c478bd9Sstevel@tonic-gate } 24767c478bd9Sstevel@tonic-gate new_gid = ctladdr->q_gid; 24777c478bd9Sstevel@tonic-gate } 24787c478bd9Sstevel@tonic-gate else 24797c478bd9Sstevel@tonic-gate { 24807c478bd9Sstevel@tonic-gate if (!DontInitGroups) 24817c478bd9Sstevel@tonic-gate { 24827c478bd9Sstevel@tonic-gate user = DefUser; 24837c478bd9Sstevel@tonic-gate if (initgroups(DefUser, DefGid) == -1 && 24847c478bd9Sstevel@tonic-gate suidwarn) 24857c478bd9Sstevel@tonic-gate { 24867c478bd9Sstevel@tonic-gate syserr("openmailer: initgroups(%s, %d) failed", 24877c478bd9Sstevel@tonic-gate DefUser, DefGid); 24887c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 24897c478bd9Sstevel@tonic-gate } 24907c478bd9Sstevel@tonic-gate } 24917c478bd9Sstevel@tonic-gate else 24927c478bd9Sstevel@tonic-gate { 24937c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 24947c478bd9Sstevel@tonic-gate 24957c478bd9Sstevel@tonic-gate gidset[0] = DefGid; 24967c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 24977c478bd9Sstevel@tonic-gate && suidwarn) 24987c478bd9Sstevel@tonic-gate { 24997c478bd9Sstevel@tonic-gate syserr("openmailer: setgroups() failed"); 25007c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 25017c478bd9Sstevel@tonic-gate } 25027c478bd9Sstevel@tonic-gate } 25037c478bd9Sstevel@tonic-gate if (m->m_gid == NO_GID) 25047c478bd9Sstevel@tonic-gate new_gid = DefGid; 25057c478bd9Sstevel@tonic-gate else 25067c478bd9Sstevel@tonic-gate new_gid = m->m_gid; 25077c478bd9Sstevel@tonic-gate } 25087c478bd9Sstevel@tonic-gate if (new_gid != NO_GID) 25097c478bd9Sstevel@tonic-gate { 25107c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && 25117c478bd9Sstevel@tonic-gate bitnset(M_SPECIFIC_UID, m->m_flags) && 25127c478bd9Sstevel@tonic-gate new_gid != getgid() && 25137c478bd9Sstevel@tonic-gate new_gid != getegid()) 25147c478bd9Sstevel@tonic-gate { 25157c478bd9Sstevel@tonic-gate /* Only root can change the gid */ 25167c478bd9Sstevel@tonic-gate syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 25177c478bd9Sstevel@tonic-gate (int) RunAsUid, (int) new_gid, 25187c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 25197c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 25207c478bd9Sstevel@tonic-gate } 25217c478bd9Sstevel@tonic-gate 25227c478bd9Sstevel@tonic-gate if (setgid(new_gid) < 0 && suidwarn) 25237c478bd9Sstevel@tonic-gate { 25247c478bd9Sstevel@tonic-gate syserr("openmailer: setgid(%ld) failed", 25257c478bd9Sstevel@tonic-gate (long) new_gid); 25267c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 25277c478bd9Sstevel@tonic-gate } 25287c478bd9Sstevel@tonic-gate } 25297c478bd9Sstevel@tonic-gate 25307c478bd9Sstevel@tonic-gate /* change root to some "safe" directory */ 25317c478bd9Sstevel@tonic-gate if (m->m_rootdir != NULL) 25327c478bd9Sstevel@tonic-gate { 2533*058561cbSjbeck expand(m->m_rootdir, cbuf, sizeof(cbuf), e); 25347c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 25357c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: chroot %s\n", 25367c478bd9Sstevel@tonic-gate cbuf); 25377c478bd9Sstevel@tonic-gate if (chroot(cbuf) < 0) 25387c478bd9Sstevel@tonic-gate { 25397c478bd9Sstevel@tonic-gate syserr("openmailer: Cannot chroot(%s)", 25407c478bd9Sstevel@tonic-gate cbuf); 25417c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 25427c478bd9Sstevel@tonic-gate } 25437c478bd9Sstevel@tonic-gate if (chdir("/") < 0) 25447c478bd9Sstevel@tonic-gate { 25457c478bd9Sstevel@tonic-gate syserr("openmailer: cannot chdir(/)"); 25467c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 25477c478bd9Sstevel@tonic-gate } 25487c478bd9Sstevel@tonic-gate } 25497c478bd9Sstevel@tonic-gate 25507c478bd9Sstevel@tonic-gate /* reset user id */ 25517c478bd9Sstevel@tonic-gate endpwent(); 25527c478bd9Sstevel@tonic-gate sm_mbdb_terminate(); 25537c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, m->m_flags)) 25547c478bd9Sstevel@tonic-gate { 25557c478bd9Sstevel@tonic-gate if (m->m_uid == NO_UID) 25567c478bd9Sstevel@tonic-gate new_euid = RunAsUid; 25577c478bd9Sstevel@tonic-gate else 25587c478bd9Sstevel@tonic-gate new_euid = m->m_uid; 25597c478bd9Sstevel@tonic-gate 25607c478bd9Sstevel@tonic-gate /* 25617c478bd9Sstevel@tonic-gate ** Undo the effects of the uid change in main 25627c478bd9Sstevel@tonic-gate ** for signal handling. The real uid may 25637c478bd9Sstevel@tonic-gate ** be used by mailer in adding a "From " 25647c478bd9Sstevel@tonic-gate ** line. 25657c478bd9Sstevel@tonic-gate */ 25667c478bd9Sstevel@tonic-gate 25677c478bd9Sstevel@tonic-gate if (RealUid != 0 && RealUid != getuid()) 25687c478bd9Sstevel@tonic-gate { 25697c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID 25707c478bd9Sstevel@tonic-gate # if HASSETREUID 25717c478bd9Sstevel@tonic-gate if (setreuid(RealUid, geteuid()) < 0) 25727c478bd9Sstevel@tonic-gate { 25737c478bd9Sstevel@tonic-gate syserr("openmailer: setreuid(%d, %d) failed", 25747c478bd9Sstevel@tonic-gate (int) RealUid, (int) geteuid()); 25757c478bd9Sstevel@tonic-gate exit(EX_OSERR); 25767c478bd9Sstevel@tonic-gate } 25777c478bd9Sstevel@tonic-gate # endif /* HASSETREUID */ 25787c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 25797c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID 25807c478bd9Sstevel@tonic-gate new_ruid = RealUid; 25817c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 25827c478bd9Sstevel@tonic-gate } 25837c478bd9Sstevel@tonic-gate } 25847c478bd9Sstevel@tonic-gate else if (bitset(S_ISUID, stb.st_mode)) 25857c478bd9Sstevel@tonic-gate new_ruid = stb.st_uid; 25867c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 25877c478bd9Sstevel@tonic-gate new_ruid = ctladdr->q_uid; 25887c478bd9Sstevel@tonic-gate else if (m->m_uid != NO_UID) 25897c478bd9Sstevel@tonic-gate new_ruid = m->m_uid; 25907c478bd9Sstevel@tonic-gate else 25917c478bd9Sstevel@tonic-gate new_ruid = DefUid; 25927c478bd9Sstevel@tonic-gate 25937c478bd9Sstevel@tonic-gate # if _FFR_USE_SETLOGIN 25947c478bd9Sstevel@tonic-gate /* run disconnected from terminal and set login name */ 25957c478bd9Sstevel@tonic-gate if (setsid() >= 0 && 25967c478bd9Sstevel@tonic-gate ctladdr != NULL && ctladdr->q_uid != 0 && 25977c478bd9Sstevel@tonic-gate new_euid == ctladdr->q_uid) 25987c478bd9Sstevel@tonic-gate { 25997c478bd9Sstevel@tonic-gate struct passwd *pwd; 26007c478bd9Sstevel@tonic-gate 26017c478bd9Sstevel@tonic-gate pwd = sm_getpwuid(ctladdr->q_uid); 26027c478bd9Sstevel@tonic-gate if (pwd != NULL && suidwarn) 26037c478bd9Sstevel@tonic-gate (void) setlogin(pwd->pw_name); 26047c478bd9Sstevel@tonic-gate endpwent(); 26057c478bd9Sstevel@tonic-gate } 26067c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SETLOGIN */ 26077c478bd9Sstevel@tonic-gate 26087c478bd9Sstevel@tonic-gate if (new_euid != NO_UID) 26097c478bd9Sstevel@tonic-gate { 26107c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && new_euid != RunAsUid) 26117c478bd9Sstevel@tonic-gate { 26127c478bd9Sstevel@tonic-gate /* Only root can change the uid */ 26137c478bd9Sstevel@tonic-gate syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 26147c478bd9Sstevel@tonic-gate (int) new_euid, (int) RunAsUid); 26157c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 26167c478bd9Sstevel@tonic-gate } 26177c478bd9Sstevel@tonic-gate 26187c478bd9Sstevel@tonic-gate vendor_set_uid(new_euid); 26197c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID 26207c478bd9Sstevel@tonic-gate if (seteuid(new_euid) < 0 && suidwarn) 26217c478bd9Sstevel@tonic-gate { 26227c478bd9Sstevel@tonic-gate syserr("openmailer: seteuid(%ld) failed", 26237c478bd9Sstevel@tonic-gate (long) new_euid); 26247c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 26257c478bd9Sstevel@tonic-gate } 26267c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 26277c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID 26287c478bd9Sstevel@tonic-gate if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 26297c478bd9Sstevel@tonic-gate { 26307c478bd9Sstevel@tonic-gate syserr("openmailer: setreuid(%ld, %ld) failed", 26317c478bd9Sstevel@tonic-gate (long) new_ruid, (long) new_euid); 26327c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 26337c478bd9Sstevel@tonic-gate } 26347c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 26357c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETUID 26367c478bd9Sstevel@tonic-gate if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 26377c478bd9Sstevel@tonic-gate { 26387c478bd9Sstevel@tonic-gate syserr("openmailer: setuid(%ld) failed", 26397c478bd9Sstevel@tonic-gate (long) new_euid); 26407c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 26417c478bd9Sstevel@tonic-gate } 26427c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETUID */ 26437c478bd9Sstevel@tonic-gate } 26447c478bd9Sstevel@tonic-gate else if (new_ruid != NO_UID) 26457c478bd9Sstevel@tonic-gate { 26467c478bd9Sstevel@tonic-gate vendor_set_uid(new_ruid); 26477c478bd9Sstevel@tonic-gate if (setuid(new_ruid) < 0 && suidwarn) 26487c478bd9Sstevel@tonic-gate { 26497c478bd9Sstevel@tonic-gate syserr("openmailer: setuid(%ld) failed", 26507c478bd9Sstevel@tonic-gate (long) new_ruid); 26517c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 26527c478bd9Sstevel@tonic-gate } 26537c478bd9Sstevel@tonic-gate } 26547c478bd9Sstevel@tonic-gate 26557c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 26567c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 26577c478bd9Sstevel@tonic-gate (int) getuid(), (int) geteuid(), 26587c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 26597c478bd9Sstevel@tonic-gate 26607c478bd9Sstevel@tonic-gate /* move into some "safe" directory */ 26617c478bd9Sstevel@tonic-gate if (m->m_execdir != NULL) 26627c478bd9Sstevel@tonic-gate { 26637c478bd9Sstevel@tonic-gate char *q; 26647c478bd9Sstevel@tonic-gate 26657c478bd9Sstevel@tonic-gate for (p = m->m_execdir; p != NULL; p = q) 26667c478bd9Sstevel@tonic-gate { 26677c478bd9Sstevel@tonic-gate q = strchr(p, ':'); 26687c478bd9Sstevel@tonic-gate if (q != NULL) 26697c478bd9Sstevel@tonic-gate *q = '\0'; 2670*058561cbSjbeck expand(p, cbuf, sizeof(cbuf), e); 26717c478bd9Sstevel@tonic-gate if (q != NULL) 26727c478bd9Sstevel@tonic-gate *q++ = ':'; 26737c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 26747c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: trydir %s\n", 26757c478bd9Sstevel@tonic-gate cbuf); 26767c478bd9Sstevel@tonic-gate if (cbuf[0] != '\0' && 26777c478bd9Sstevel@tonic-gate chdir(cbuf) >= 0) 26787c478bd9Sstevel@tonic-gate break; 26797c478bd9Sstevel@tonic-gate } 26807c478bd9Sstevel@tonic-gate } 26817c478bd9Sstevel@tonic-gate 26827c478bd9Sstevel@tonic-gate /* Check safety of program to be run */ 26837c478bd9Sstevel@tonic-gate sff = SFF_ROOTOK|SFF_EXECOK; 26847c478bd9Sstevel@tonic-gate if (!bitnset(DBS_RUNWRITABLEPROGRAM, 26857c478bd9Sstevel@tonic-gate DontBlameSendmail)) 26867c478bd9Sstevel@tonic-gate sff |= SFF_NOGWFILES|SFF_NOWWFILES; 26877c478bd9Sstevel@tonic-gate if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 26887c478bd9Sstevel@tonic-gate DontBlameSendmail)) 26897c478bd9Sstevel@tonic-gate sff |= SFF_NOPATHCHECK; 26907c478bd9Sstevel@tonic-gate else 26917c478bd9Sstevel@tonic-gate sff |= SFF_SAFEDIRPATH; 26927c478bd9Sstevel@tonic-gate ret = safefile(m->m_mailer, getuid(), getgid(), 26937c478bd9Sstevel@tonic-gate user, sff, 0, NULL); 26947c478bd9Sstevel@tonic-gate if (ret != 0) 26957c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 26967c478bd9Sstevel@tonic-gate "Warning: program %s unsafe: %s", 26977c478bd9Sstevel@tonic-gate m->m_mailer, sm_errstring(ret)); 26987c478bd9Sstevel@tonic-gate 26997c478bd9Sstevel@tonic-gate /* arrange to filter std & diag output of command */ 27007c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 27017c478bd9Sstevel@tonic-gate if (dup2(rpvect[1], STDOUT_FILENO) < 0) 27027c478bd9Sstevel@tonic-gate { 27037c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 27047c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 27057c478bd9Sstevel@tonic-gate m->m_name, rpvect[1]); 27067c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 27077c478bd9Sstevel@tonic-gate } 27087c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 27097c478bd9Sstevel@tonic-gate 27107c478bd9Sstevel@tonic-gate if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 27117c478bd9Sstevel@tonic-gate { 27127c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup stdout for stderr", 27137c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 27147c478bd9Sstevel@tonic-gate m->m_name); 27157c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 27167c478bd9Sstevel@tonic-gate } 27177c478bd9Sstevel@tonic-gate 27187c478bd9Sstevel@tonic-gate /* arrange to get standard input */ 27197c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 27207c478bd9Sstevel@tonic-gate if (dup2(mpvect[0], STDIN_FILENO) < 0) 27217c478bd9Sstevel@tonic-gate { 27227c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 27237c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 27247c478bd9Sstevel@tonic-gate m->m_name, mpvect[0]); 27257c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 27267c478bd9Sstevel@tonic-gate } 27277c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 27287c478bd9Sstevel@tonic-gate 27297c478bd9Sstevel@tonic-gate /* arrange for all the files to be closed */ 27307c478bd9Sstevel@tonic-gate sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 27317c478bd9Sstevel@tonic-gate 27327c478bd9Sstevel@tonic-gate # if !_FFR_USE_SETLOGIN 27337c478bd9Sstevel@tonic-gate /* run disconnected from terminal */ 27347c478bd9Sstevel@tonic-gate (void) setsid(); 27357c478bd9Sstevel@tonic-gate # endif /* !_FFR_USE_SETLOGIN */ 27367c478bd9Sstevel@tonic-gate 27377c478bd9Sstevel@tonic-gate /* try to execute the mailer */ 27387c478bd9Sstevel@tonic-gate (void) execve(m->m_mailer, (ARGV_T) pv, 27397c478bd9Sstevel@tonic-gate (ARGV_T) UserEnviron); 27407c478bd9Sstevel@tonic-gate save_errno = errno; 27417c478bd9Sstevel@tonic-gate syserr("Cannot exec %s", m->m_mailer); 27427c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) || 27437c478bd9Sstevel@tonic-gate transienterror(save_errno)) 27447c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 27457c478bd9Sstevel@tonic-gate _exit(EX_UNAVAILABLE); 27467c478bd9Sstevel@tonic-gate } 27477c478bd9Sstevel@tonic-gate 27487c478bd9Sstevel@tonic-gate /* 27497c478bd9Sstevel@tonic-gate ** Set up return value. 27507c478bd9Sstevel@tonic-gate */ 27517c478bd9Sstevel@tonic-gate 27527c478bd9Sstevel@tonic-gate if (mci == NULL) 27537c478bd9Sstevel@tonic-gate { 27547c478bd9Sstevel@tonic-gate if (clever) 27557c478bd9Sstevel@tonic-gate { 27567c478bd9Sstevel@tonic-gate /* 27577c478bd9Sstevel@tonic-gate ** Allocate from general heap, not 27587c478bd9Sstevel@tonic-gate ** envelope rpool, because this mci 27597c478bd9Sstevel@tonic-gate ** is going to be cached. 27607c478bd9Sstevel@tonic-gate */ 27617c478bd9Sstevel@tonic-gate 27627c478bd9Sstevel@tonic-gate mci = mci_new(NULL); 27637c478bd9Sstevel@tonic-gate } 27647c478bd9Sstevel@tonic-gate else 27657c478bd9Sstevel@tonic-gate { 27667c478bd9Sstevel@tonic-gate /* 27677c478bd9Sstevel@tonic-gate ** Prevent a storage leak by allocating 27687c478bd9Sstevel@tonic-gate ** this from the envelope rpool. 27697c478bd9Sstevel@tonic-gate */ 27707c478bd9Sstevel@tonic-gate 27717c478bd9Sstevel@tonic-gate mci = mci_new(e->e_rpool); 27727c478bd9Sstevel@tonic-gate } 27737c478bd9Sstevel@tonic-gate } 27747c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 27757c478bd9Sstevel@tonic-gate if (clever) 27767c478bd9Sstevel@tonic-gate { 27777c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 27787c478bd9Sstevel@tonic-gate mci_cache(mci); 27797c478bd9Sstevel@tonic-gate } 27807c478bd9Sstevel@tonic-gate else 27817c478bd9Sstevel@tonic-gate { 27827c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 27837c478bd9Sstevel@tonic-gate } 27847c478bd9Sstevel@tonic-gate mci->mci_pid = pid; 27857c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 27867c478bd9Sstevel@tonic-gate mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 27877c478bd9Sstevel@tonic-gate (void *) &(mpvect[1]), SM_IO_WRONLY_B, 27887c478bd9Sstevel@tonic-gate NULL); 27897c478bd9Sstevel@tonic-gate if (mci->mci_out == NULL) 27907c478bd9Sstevel@tonic-gate { 27917c478bd9Sstevel@tonic-gate syserr("deliver: cannot create mailer output channel, fd=%d", 27927c478bd9Sstevel@tonic-gate mpvect[1]); 27937c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 27947c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 27957c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 27967c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 27977c478bd9Sstevel@tonic-gate goto give_up; 27987c478bd9Sstevel@tonic-gate } 27997c478bd9Sstevel@tonic-gate 28007c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 28017c478bd9Sstevel@tonic-gate mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 28027c478bd9Sstevel@tonic-gate (void *) &(rpvect[0]), SM_IO_RDONLY_B, 28037c478bd9Sstevel@tonic-gate NULL); 28047c478bd9Sstevel@tonic-gate if (mci->mci_in == NULL) 28057c478bd9Sstevel@tonic-gate { 28067c478bd9Sstevel@tonic-gate syserr("deliver: cannot create mailer input channel, fd=%d", 28077c478bd9Sstevel@tonic-gate mpvect[1]); 28087c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 28097c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 28107c478bd9Sstevel@tonic-gate mci->mci_out = NULL; 28117c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 28127c478bd9Sstevel@tonic-gate goto give_up; 28137c478bd9Sstevel@tonic-gate } 28147c478bd9Sstevel@tonic-gate } 28157c478bd9Sstevel@tonic-gate 28167c478bd9Sstevel@tonic-gate /* 28177c478bd9Sstevel@tonic-gate ** If we are in SMTP opening state, send initial protocol. 28187c478bd9Sstevel@tonic-gate */ 28197c478bd9Sstevel@tonic-gate 28207c478bd9Sstevel@tonic-gate if (bitnset(M_7BITS, m->m_flags) && 28217c478bd9Sstevel@tonic-gate (!clever || mci->mci_state == MCIS_OPENING)) 28227c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_7BIT; 28237c478bd9Sstevel@tonic-gate if (clever && mci->mci_state != MCIS_CLOSED) 28247c478bd9Sstevel@tonic-gate { 28257c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 28267c478bd9Sstevel@tonic-gate int dotpos; 28277c478bd9Sstevel@tonic-gate char *srvname; 28287c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 28297c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 28307c478bd9Sstevel@tonic-gate 28317c478bd9Sstevel@tonic-gate # if SASL 28327c478bd9Sstevel@tonic-gate # define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 28337c478bd9Sstevel@tonic-gate # endif /* SASL */ 28347c478bd9Sstevel@tonic-gate # if STARTTLS 28357c478bd9Sstevel@tonic-gate # define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 28367c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 28377c478bd9Sstevel@tonic-gate # define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 28387c478bd9Sstevel@tonic-gate # define SET_HELO(f) f |= MCIF_ONLY_EHLO 28397c478bd9Sstevel@tonic-gate # define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 28407c478bd9Sstevel@tonic-gate 28417c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 28427c478bd9Sstevel@tonic-gate /* don't use CurHostName, it is changed in many places */ 28437c478bd9Sstevel@tonic-gate if (mci->mci_host != NULL) 28447c478bd9Sstevel@tonic-gate { 28457c478bd9Sstevel@tonic-gate srvname = mci->mci_host; 28467c478bd9Sstevel@tonic-gate dotpos = strlen(srvname) - 1; 28477c478bd9Sstevel@tonic-gate if (dotpos >= 0) 28487c478bd9Sstevel@tonic-gate { 28497c478bd9Sstevel@tonic-gate if (srvname[dotpos] == '.') 28507c478bd9Sstevel@tonic-gate srvname[dotpos] = '\0'; 28517c478bd9Sstevel@tonic-gate else 28527c478bd9Sstevel@tonic-gate dotpos = -1; 28537c478bd9Sstevel@tonic-gate } 28547c478bd9Sstevel@tonic-gate } 28557c478bd9Sstevel@tonic-gate else if (mci->mci_mailer != NULL) 28567c478bd9Sstevel@tonic-gate { 28577c478bd9Sstevel@tonic-gate srvname = mci->mci_mailer->m_name; 28587c478bd9Sstevel@tonic-gate dotpos = -1; 28597c478bd9Sstevel@tonic-gate } 28607c478bd9Sstevel@tonic-gate else 28617c478bd9Sstevel@tonic-gate { 28627c478bd9Sstevel@tonic-gate srvname = "local"; 28637c478bd9Sstevel@tonic-gate dotpos = -1; 28647c478bd9Sstevel@tonic-gate } 28657c478bd9Sstevel@tonic-gate 28667c478bd9Sstevel@tonic-gate /* don't set {server_name} to NULL or "": see getauth() */ 28677c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 28687c478bd9Sstevel@tonic-gate srvname); 28697c478bd9Sstevel@tonic-gate 28707c478bd9Sstevel@tonic-gate /* CurHostAddr is set by makeconnection() and mci_get() */ 28717c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 28727c478bd9Sstevel@tonic-gate { 28737c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_TEMP, 28747c478bd9Sstevel@tonic-gate macid("{server_addr}"), 28757c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 28767c478bd9Sstevel@tonic-gate } 28777c478bd9Sstevel@tonic-gate else if (mci->mci_mailer != NULL) 28787c478bd9Sstevel@tonic-gate { 28797c478bd9Sstevel@tonic-gate /* mailer name is unique, use it as address */ 28807c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_PERM, 28817c478bd9Sstevel@tonic-gate macid("{server_addr}"), 28827c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name); 28837c478bd9Sstevel@tonic-gate } 28847c478bd9Sstevel@tonic-gate else 28857c478bd9Sstevel@tonic-gate { 28867c478bd9Sstevel@tonic-gate /* don't set it to NULL or "": see getauth() */ 28877c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_PERM, 28887c478bd9Sstevel@tonic-gate macid("{server_addr}"), "0"); 28897c478bd9Sstevel@tonic-gate } 28907c478bd9Sstevel@tonic-gate 28917c478bd9Sstevel@tonic-gate /* undo change of srvname (mci->mci_host) */ 28927c478bd9Sstevel@tonic-gate if (dotpos >= 0) 28937c478bd9Sstevel@tonic-gate srvname[dotpos] = '.'; 28947c478bd9Sstevel@tonic-gate 28957c478bd9Sstevel@tonic-gate reconnect: /* after switching to an encrypted connection */ 28967c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 28977c478bd9Sstevel@tonic-gate 28987c478bd9Sstevel@tonic-gate /* set the current connection information */ 28997c478bd9Sstevel@tonic-gate e->e_mci = mci; 29007c478bd9Sstevel@tonic-gate # if SASL 29017c478bd9Sstevel@tonic-gate mci->mci_saslcap = NULL; 29027c478bd9Sstevel@tonic-gate # endif /* SASL */ 29037c478bd9Sstevel@tonic-gate smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 29047c478bd9Sstevel@tonic-gate CLR_HELO(mci->mci_flags); 29057c478bd9Sstevel@tonic-gate 29067c478bd9Sstevel@tonic-gate if (IS_DLVR_RETURN(e)) 29077c478bd9Sstevel@tonic-gate { 29087c478bd9Sstevel@tonic-gate /* 29097c478bd9Sstevel@tonic-gate ** Check whether other side can deliver e-mail 29107c478bd9Sstevel@tonic-gate ** fast enough 29117c478bd9Sstevel@tonic-gate */ 29127c478bd9Sstevel@tonic-gate 29137c478bd9Sstevel@tonic-gate if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 29147c478bd9Sstevel@tonic-gate { 29157c478bd9Sstevel@tonic-gate e->e_status = "5.4.7"; 29167c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 29177c478bd9Sstevel@tonic-gate "554 Server does not support Deliver By"); 29187c478bd9Sstevel@tonic-gate rcode = EX_UNAVAILABLE; 29197c478bd9Sstevel@tonic-gate goto give_up; 29207c478bd9Sstevel@tonic-gate } 29217c478bd9Sstevel@tonic-gate if (e->e_deliver_by > 0 && 29227c478bd9Sstevel@tonic-gate e->e_deliver_by - (curtime() - e->e_ctime) < 29237c478bd9Sstevel@tonic-gate mci->mci_min_by) 29247c478bd9Sstevel@tonic-gate { 29257c478bd9Sstevel@tonic-gate e->e_status = "5.4.7"; 29267c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 29277c478bd9Sstevel@tonic-gate "554 Message can't be delivered in time; %ld < %ld", 29287c478bd9Sstevel@tonic-gate e->e_deliver_by - (curtime() - e->e_ctime), 29297c478bd9Sstevel@tonic-gate mci->mci_min_by); 29307c478bd9Sstevel@tonic-gate rcode = EX_UNAVAILABLE; 29317c478bd9Sstevel@tonic-gate goto give_up; 29327c478bd9Sstevel@tonic-gate } 29337c478bd9Sstevel@tonic-gate } 29347c478bd9Sstevel@tonic-gate 29357c478bd9Sstevel@tonic-gate # if STARTTLS 29367c478bd9Sstevel@tonic-gate /* first TLS then AUTH to provide a security layer */ 29377c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED && 29387c478bd9Sstevel@tonic-gate !DONE_STARTTLS(mci->mci_flags)) 29397c478bd9Sstevel@tonic-gate { 29407c478bd9Sstevel@tonic-gate int olderrors; 29417c478bd9Sstevel@tonic-gate bool usetls; 29427c478bd9Sstevel@tonic-gate bool saveQuickAbort = QuickAbort; 29437c478bd9Sstevel@tonic-gate bool saveSuprErrs = SuprErrs; 29447c478bd9Sstevel@tonic-gate char *host = NULL; 29457c478bd9Sstevel@tonic-gate 29467c478bd9Sstevel@tonic-gate rcode = EX_OK; 29477c478bd9Sstevel@tonic-gate usetls = bitset(MCIF_TLS, mci->mci_flags); 29487c478bd9Sstevel@tonic-gate if (usetls) 29497c478bd9Sstevel@tonic-gate usetls = !iscltflgset(e, D_NOTLS); 29507c478bd9Sstevel@tonic-gate 2951*058561cbSjbeck host = macvalue(macid("{server_name}"), e); 29527c478bd9Sstevel@tonic-gate if (usetls) 29537c478bd9Sstevel@tonic-gate { 29547c478bd9Sstevel@tonic-gate olderrors = Errors; 29557c478bd9Sstevel@tonic-gate QuickAbort = false; 29567c478bd9Sstevel@tonic-gate SuprErrs = true; 29577c478bd9Sstevel@tonic-gate if (rscheck("try_tls", host, NULL, e, 2958*058561cbSjbeck RSF_RMCOMM, 7, host, NOQID, NULL) 2959*058561cbSjbeck != EX_OK 29607c478bd9Sstevel@tonic-gate || Errors > olderrors) 2961*058561cbSjbeck { 29627c478bd9Sstevel@tonic-gate usetls = false; 2963*058561cbSjbeck } 29647c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 29657c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 29667c478bd9Sstevel@tonic-gate } 29677c478bd9Sstevel@tonic-gate 29687c478bd9Sstevel@tonic-gate if (usetls) 29697c478bd9Sstevel@tonic-gate { 29707c478bd9Sstevel@tonic-gate if ((rcode = starttls(m, mci, e)) == EX_OK) 29717c478bd9Sstevel@tonic-gate { 29727c478bd9Sstevel@tonic-gate /* start again without STARTTLS */ 29737c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_TLSACT; 29747c478bd9Sstevel@tonic-gate } 29757c478bd9Sstevel@tonic-gate else 29767c478bd9Sstevel@tonic-gate { 29777c478bd9Sstevel@tonic-gate char *s; 29787c478bd9Sstevel@tonic-gate 29797c478bd9Sstevel@tonic-gate /* 29807c478bd9Sstevel@tonic-gate ** TLS negotation failed, what to do? 29817c478bd9Sstevel@tonic-gate ** fall back to unencrypted connection 29827c478bd9Sstevel@tonic-gate ** or abort? How to decide? 29837c478bd9Sstevel@tonic-gate ** set a macro and call a ruleset. 29847c478bd9Sstevel@tonic-gate */ 29857c478bd9Sstevel@tonic-gate 29867c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLS; 29877c478bd9Sstevel@tonic-gate switch (rcode) 29887c478bd9Sstevel@tonic-gate { 29897c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 29907c478bd9Sstevel@tonic-gate s = "TEMP"; 29917c478bd9Sstevel@tonic-gate break; 29927c478bd9Sstevel@tonic-gate case EX_USAGE: 29937c478bd9Sstevel@tonic-gate s = "USAGE"; 29947c478bd9Sstevel@tonic-gate break; 29957c478bd9Sstevel@tonic-gate case EX_PROTOCOL: 29967c478bd9Sstevel@tonic-gate s = "PROTOCOL"; 29977c478bd9Sstevel@tonic-gate break; 29987c478bd9Sstevel@tonic-gate case EX_SOFTWARE: 29997c478bd9Sstevel@tonic-gate s = "SOFTWARE"; 30007c478bd9Sstevel@tonic-gate break; 3001445f2479Sjbeck case EX_UNAVAILABLE: 3002445f2479Sjbeck s = "NONE"; 3003445f2479Sjbeck break; 30047c478bd9Sstevel@tonic-gate 30057c478bd9Sstevel@tonic-gate /* everything else is a failure */ 30067c478bd9Sstevel@tonic-gate default: 30077c478bd9Sstevel@tonic-gate s = "FAILURE"; 30087c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 30097c478bd9Sstevel@tonic-gate } 30107c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 30117c478bd9Sstevel@tonic-gate macid("{verify}"), s); 30127c478bd9Sstevel@tonic-gate } 30137c478bd9Sstevel@tonic-gate } 30147c478bd9Sstevel@tonic-gate else 30157c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 30167c478bd9Sstevel@tonic-gate macid("{verify}"), "NONE"); 30177c478bd9Sstevel@tonic-gate olderrors = Errors; 30187c478bd9Sstevel@tonic-gate QuickAbort = false; 30197c478bd9Sstevel@tonic-gate SuprErrs = true; 30207c478bd9Sstevel@tonic-gate 30217c478bd9Sstevel@tonic-gate /* 30227c478bd9Sstevel@tonic-gate ** rcode == EX_SOFTWARE is special: 30237c478bd9Sstevel@tonic-gate ** the TLS negotation failed 30247c478bd9Sstevel@tonic-gate ** we have to drop the connection no matter what 30257c478bd9Sstevel@tonic-gate ** However, we call tls_server to give it the chance 30267c478bd9Sstevel@tonic-gate ** to log the problem and return an appropriate 30277c478bd9Sstevel@tonic-gate ** error code. 30287c478bd9Sstevel@tonic-gate */ 30297c478bd9Sstevel@tonic-gate 30307c478bd9Sstevel@tonic-gate if (rscheck("tls_server", 30317c478bd9Sstevel@tonic-gate macvalue(macid("{verify}"), e), 30327c478bd9Sstevel@tonic-gate NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3033*058561cbSjbeck host, NOQID, NULL) != EX_OK || 30347c478bd9Sstevel@tonic-gate Errors > olderrors || 30357c478bd9Sstevel@tonic-gate rcode == EX_SOFTWARE) 30367c478bd9Sstevel@tonic-gate { 30377c478bd9Sstevel@tonic-gate char enhsc[ENHSCLEN]; 30387c478bd9Sstevel@tonic-gate extern char MsgBuf[]; 30397c478bd9Sstevel@tonic-gate 30407c478bd9Sstevel@tonic-gate if (ISSMTPCODE(MsgBuf) && 30417c478bd9Sstevel@tonic-gate extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 30427c478bd9Sstevel@tonic-gate { 30437c478bd9Sstevel@tonic-gate p = sm_rpool_strdup_x(e->e_rpool, 30447c478bd9Sstevel@tonic-gate MsgBuf); 30457c478bd9Sstevel@tonic-gate } 30467c478bd9Sstevel@tonic-gate else 30477c478bd9Sstevel@tonic-gate { 30487c478bd9Sstevel@tonic-gate p = "403 4.7.0 server not authenticated."; 30497c478bd9Sstevel@tonic-gate (void) sm_strlcpy(enhsc, "4.7.0", 3050*058561cbSjbeck sizeof(enhsc)); 30517c478bd9Sstevel@tonic-gate } 30527c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 30537c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 30547c478bd9Sstevel@tonic-gate 30557c478bd9Sstevel@tonic-gate if (rcode == EX_SOFTWARE) 30567c478bd9Sstevel@tonic-gate { 30577c478bd9Sstevel@tonic-gate /* drop the connection */ 30587c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_QUITING; 30597c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL) 30607c478bd9Sstevel@tonic-gate { 30617c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_in, 30627c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT); 30637c478bd9Sstevel@tonic-gate mci->mci_in = NULL; 30647c478bd9Sstevel@tonic-gate } 30657c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLSACT; 30667c478bd9Sstevel@tonic-gate (void) endmailer(mci, e, pv); 30677c478bd9Sstevel@tonic-gate } 30687c478bd9Sstevel@tonic-gate else 30697c478bd9Sstevel@tonic-gate { 30707c478bd9Sstevel@tonic-gate /* abort transfer */ 30717c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 30727c478bd9Sstevel@tonic-gate } 30737c478bd9Sstevel@tonic-gate 30747c478bd9Sstevel@tonic-gate /* avoid bogus error msg */ 30757c478bd9Sstevel@tonic-gate mci->mci_errno = 0; 30767c478bd9Sstevel@tonic-gate 30777c478bd9Sstevel@tonic-gate /* temp or permanent failure? */ 30787c478bd9Sstevel@tonic-gate rcode = (*p == '4') ? EX_TEMPFAIL 30797c478bd9Sstevel@tonic-gate : EX_UNAVAILABLE; 30807c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, enhsc, p); 30817c478bd9Sstevel@tonic-gate 30827c478bd9Sstevel@tonic-gate /* 30837c478bd9Sstevel@tonic-gate ** hack to get the error message into 30847c478bd9Sstevel@tonic-gate ** the envelope (done in giveresponse()) 30857c478bd9Sstevel@tonic-gate */ 30867c478bd9Sstevel@tonic-gate 30877c478bd9Sstevel@tonic-gate (void) sm_strlcpy(SmtpError, p, 3088*058561cbSjbeck sizeof(SmtpError)); 30897c478bd9Sstevel@tonic-gate } 30907c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 30917c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 30927c478bd9Sstevel@tonic-gate if (DONE_STARTTLS(mci->mci_flags) && 30937c478bd9Sstevel@tonic-gate mci->mci_state != MCIS_CLOSED) 30947c478bd9Sstevel@tonic-gate { 30957c478bd9Sstevel@tonic-gate SET_HELO(mci->mci_flags); 30967c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_EXTENS; 30977c478bd9Sstevel@tonic-gate goto reconnect; 30987c478bd9Sstevel@tonic-gate } 30997c478bd9Sstevel@tonic-gate } 31007c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 31017c478bd9Sstevel@tonic-gate # if SASL 31027c478bd9Sstevel@tonic-gate /* if other server supports authentication let's authenticate */ 31037c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED && 31047c478bd9Sstevel@tonic-gate mci->mci_saslcap != NULL && 31057c478bd9Sstevel@tonic-gate !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 31067c478bd9Sstevel@tonic-gate { 31077c478bd9Sstevel@tonic-gate /* Should we require some minimum authentication? */ 31087c478bd9Sstevel@tonic-gate if ((ret = smtpauth(m, mci, e)) == EX_OK) 31097c478bd9Sstevel@tonic-gate { 31107c478bd9Sstevel@tonic-gate int result; 31117c478bd9Sstevel@tonic-gate sasl_ssf_t *ssf = NULL; 31127c478bd9Sstevel@tonic-gate 31137c478bd9Sstevel@tonic-gate /* Get security strength (features) */ 31147c478bd9Sstevel@tonic-gate result = sasl_getprop(mci->mci_conn, SASL_SSF, 31157c478bd9Sstevel@tonic-gate # if SASL >= 20000 31167c478bd9Sstevel@tonic-gate (const void **) &ssf); 31177c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */ 31187c478bd9Sstevel@tonic-gate (void **) &ssf); 31197c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */ 31207c478bd9Sstevel@tonic-gate 31217c478bd9Sstevel@tonic-gate /* XXX authid? */ 31227c478bd9Sstevel@tonic-gate if (LogLevel > 9) 31237c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, 31247c478bd9Sstevel@tonic-gate "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 31257c478bd9Sstevel@tonic-gate mci->mci_host, 31267c478bd9Sstevel@tonic-gate macvalue(macid("{auth_type}"), e), 31277c478bd9Sstevel@tonic-gate result == SASL_OK ? *ssf : 0); 31287c478bd9Sstevel@tonic-gate 31297c478bd9Sstevel@tonic-gate /* 31307c478bd9Sstevel@tonic-gate ** Only switch to encrypted connection 31317c478bd9Sstevel@tonic-gate ** if a security layer has been negotiated 31327c478bd9Sstevel@tonic-gate */ 31337c478bd9Sstevel@tonic-gate 31347c478bd9Sstevel@tonic-gate if (result == SASL_OK && *ssf > 0) 31357c478bd9Sstevel@tonic-gate { 31363ee0e492Sjbeck int tmo; 31373ee0e492Sjbeck 31387c478bd9Sstevel@tonic-gate /* 31397c478bd9Sstevel@tonic-gate ** Convert I/O layer to use SASL. 31407c478bd9Sstevel@tonic-gate ** If the call fails, the connection 31417c478bd9Sstevel@tonic-gate ** is aborted. 31427c478bd9Sstevel@tonic-gate */ 31437c478bd9Sstevel@tonic-gate 31443ee0e492Sjbeck tmo = DATA_PROGRESS_TIMEOUT * 1000; 31457c478bd9Sstevel@tonic-gate if (sfdcsasl(&mci->mci_in, 31467c478bd9Sstevel@tonic-gate &mci->mci_out, 31473ee0e492Sjbeck mci->mci_conn, tmo) == 0) 31487c478bd9Sstevel@tonic-gate { 31497c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_EXTENS; 31507c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_AUTHACT| 31517c478bd9Sstevel@tonic-gate MCIF_ONLY_EHLO; 31527c478bd9Sstevel@tonic-gate goto reconnect; 31537c478bd9Sstevel@tonic-gate } 31547c478bd9Sstevel@tonic-gate syserr("AUTH TLS switch failed in client"); 31557c478bd9Sstevel@tonic-gate } 31567c478bd9Sstevel@tonic-gate /* else? XXX */ 31577c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_AUTHACT; 31587c478bd9Sstevel@tonic-gate 31597c478bd9Sstevel@tonic-gate } 31607c478bd9Sstevel@tonic-gate else if (ret == EX_TEMPFAIL) 31617c478bd9Sstevel@tonic-gate { 31627c478bd9Sstevel@tonic-gate if (LogLevel > 8) 31637c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 31647c478bd9Sstevel@tonic-gate "AUTH=client, relay=%.100s, temporary failure, connection abort", 31657c478bd9Sstevel@tonic-gate mci->mci_host); 31667c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 31677c478bd9Sstevel@tonic-gate 31687c478bd9Sstevel@tonic-gate /* avoid bogus error msg */ 31697c478bd9Sstevel@tonic-gate mci->mci_errno = 0; 31707c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 31717c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, "4.3.0", p); 31727c478bd9Sstevel@tonic-gate 31737c478bd9Sstevel@tonic-gate /* 31747c478bd9Sstevel@tonic-gate ** hack to get the error message into 31757c478bd9Sstevel@tonic-gate ** the envelope (done in giveresponse()) 31767c478bd9Sstevel@tonic-gate */ 31777c478bd9Sstevel@tonic-gate 31787c478bd9Sstevel@tonic-gate (void) sm_strlcpy(SmtpError, 31797c478bd9Sstevel@tonic-gate "Temporary AUTH failure", 3180*058561cbSjbeck sizeof(SmtpError)); 31817c478bd9Sstevel@tonic-gate } 31827c478bd9Sstevel@tonic-gate } 31837c478bd9Sstevel@tonic-gate # endif /* SASL */ 31847c478bd9Sstevel@tonic-gate } 31857c478bd9Sstevel@tonic-gate 31867c478bd9Sstevel@tonic-gate 31877c478bd9Sstevel@tonic-gate do_transfer: 31887c478bd9Sstevel@tonic-gate /* clear out per-message flags from connection structure */ 31897c478bd9Sstevel@tonic-gate mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 31907c478bd9Sstevel@tonic-gate 31917c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 31927c478bd9Sstevel@tonic-gate !bitset(EF_DONT_MIME, e->e_flags) && 31937c478bd9Sstevel@tonic-gate bitnset(M_7BITS, m->m_flags)) 31947c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_CVT8TO7; 31957c478bd9Sstevel@tonic-gate 31967c478bd9Sstevel@tonic-gate #if MIME7TO8 31977c478bd9Sstevel@tonic-gate if (bitnset(M_MAKE8BIT, m->m_flags) && 31987c478bd9Sstevel@tonic-gate !bitset(MCIF_7BIT, mci->mci_flags) && 31997c478bd9Sstevel@tonic-gate (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 32007c478bd9Sstevel@tonic-gate (sm_strcasecmp(p, "quoted-printable") == 0 || 32017c478bd9Sstevel@tonic-gate sm_strcasecmp(p, "base64") == 0) && 32027c478bd9Sstevel@tonic-gate (p = hvalue("Content-Type", e->e_header)) != NULL) 32037c478bd9Sstevel@tonic-gate { 32047c478bd9Sstevel@tonic-gate /* may want to convert 7 -> 8 */ 32057c478bd9Sstevel@tonic-gate /* XXX should really parse it here -- and use a class XXX */ 32067c478bd9Sstevel@tonic-gate if (sm_strncasecmp(p, "text/plain", 10) == 0 && 32077c478bd9Sstevel@tonic-gate (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 32087c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_CVT7TO8; 32097c478bd9Sstevel@tonic-gate } 32107c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */ 32117c478bd9Sstevel@tonic-gate 32127c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 32137c478bd9Sstevel@tonic-gate { 32147c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: "); 32157c478bd9Sstevel@tonic-gate mci_dump(sm_debug_file(), mci, false); 32167c478bd9Sstevel@tonic-gate } 32177c478bd9Sstevel@tonic-gate 32187c478bd9Sstevel@tonic-gate #if _FFR_CLIENT_SIZE 32197c478bd9Sstevel@tonic-gate /* 32207c478bd9Sstevel@tonic-gate ** See if we know the maximum size and 32217c478bd9Sstevel@tonic-gate ** abort if the message is too big. 32227c478bd9Sstevel@tonic-gate ** 32237c478bd9Sstevel@tonic-gate ** NOTE: _FFR_CLIENT_SIZE is untested. 32247c478bd9Sstevel@tonic-gate */ 32257c478bd9Sstevel@tonic-gate 32267c478bd9Sstevel@tonic-gate if (bitset(MCIF_SIZE, mci->mci_flags) && 32277c478bd9Sstevel@tonic-gate mci->mci_maxsize > 0 && 32287c478bd9Sstevel@tonic-gate e->e_msgsize > mci->mci_maxsize) 32297c478bd9Sstevel@tonic-gate { 32307c478bd9Sstevel@tonic-gate e->e_flags |= EF_NO_BODY_RETN; 32317c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags)) 32327c478bd9Sstevel@tonic-gate e->e_status = "5.2.3"; 32337c478bd9Sstevel@tonic-gate else 32347c478bd9Sstevel@tonic-gate e->e_status = "5.3.4"; 32357c478bd9Sstevel@tonic-gate 32367c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 32377c478bd9Sstevel@tonic-gate "552 Message is too large; %ld bytes max", 32387c478bd9Sstevel@tonic-gate mci->mci_maxsize); 32397c478bd9Sstevel@tonic-gate rcode = EX_DATAERR; 32407c478bd9Sstevel@tonic-gate 32417c478bd9Sstevel@tonic-gate /* Need an e_message for error */ 3242*058561cbSjbeck (void) sm_snprintf(SmtpError, sizeof(SmtpError), 32437c478bd9Sstevel@tonic-gate "Message is too large; %ld bytes max", 32447c478bd9Sstevel@tonic-gate mci->mci_maxsize); 32457c478bd9Sstevel@tonic-gate goto give_up; 32467c478bd9Sstevel@tonic-gate } 32477c478bd9Sstevel@tonic-gate #endif /* _FFR_CLIENT_SIZE */ 32487c478bd9Sstevel@tonic-gate 32497c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_OPEN) 32507c478bd9Sstevel@tonic-gate { 32517c478bd9Sstevel@tonic-gate /* couldn't open the mailer */ 32527c478bd9Sstevel@tonic-gate rcode = mci->mci_exitstat; 32537c478bd9Sstevel@tonic-gate errno = mci->mci_errno; 32547c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(mci->mci_herrno); 32557c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 32567c478bd9Sstevel@tonic-gate { 32577c478bd9Sstevel@tonic-gate /* shouldn't happen */ 32587c478bd9Sstevel@tonic-gate syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 32597c478bd9Sstevel@tonic-gate (unsigned long) mci, rcode, errno, 32607c478bd9Sstevel@tonic-gate mci->mci_state, firstsig); 32617c478bd9Sstevel@tonic-gate mci_dump_all(smioout, true); 32627c478bd9Sstevel@tonic-gate rcode = EX_SOFTWARE; 32637c478bd9Sstevel@tonic-gate } 32647c478bd9Sstevel@tonic-gate else if (nummxhosts > hostnum) 32657c478bd9Sstevel@tonic-gate { 32667c478bd9Sstevel@tonic-gate /* try next MX site */ 32677c478bd9Sstevel@tonic-gate goto tryhost; 32687c478bd9Sstevel@tonic-gate } 32697c478bd9Sstevel@tonic-gate } 32707c478bd9Sstevel@tonic-gate else if (!clever) 32717c478bd9Sstevel@tonic-gate { 3272445f2479Sjbeck bool ok; 3273445f2479Sjbeck 32747c478bd9Sstevel@tonic-gate /* 32757c478bd9Sstevel@tonic-gate ** Format and send message. 32767c478bd9Sstevel@tonic-gate */ 32777c478bd9Sstevel@tonic-gate 3278445f2479Sjbeck rcode = EX_OK; 3279445f2479Sjbeck errno = 0; 3280445f2479Sjbeck ok = putfromline(mci, e); 3281445f2479Sjbeck if (ok) 3282445f2479Sjbeck ok = (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3283445f2479Sjbeck if (ok) 3284445f2479Sjbeck ok = (*e->e_putbody)(mci, e, NULL); 32857c478bd9Sstevel@tonic-gate 3286445f2479Sjbeck /* 3287445f2479Sjbeck ** Ignore an I/O error that was caused by EPIPE. 3288445f2479Sjbeck ** Some broken mailers don't read the entire body 3289445f2479Sjbeck ** but just exit() thus causing an I/O error. 3290445f2479Sjbeck */ 3291445f2479Sjbeck 3292445f2479Sjbeck if (!ok && (sm_io_error(mci->mci_out) && errno == EPIPE)) 3293445f2479Sjbeck ok = true; 3294445f2479Sjbeck 3295445f2479Sjbeck /* (always) get the exit status */ 32967c478bd9Sstevel@tonic-gate rcode = endmailer(mci, e, pv); 3297445f2479Sjbeck if (!ok) 3298445f2479Sjbeck rcode = EX_TEMPFAIL; 32997c478bd9Sstevel@tonic-gate if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 33007c478bd9Sstevel@tonic-gate { 33017c478bd9Sstevel@tonic-gate /* 33027c478bd9Sstevel@tonic-gate ** Need an e_message for mailq display. 33037c478bd9Sstevel@tonic-gate ** We set SmtpError as 33047c478bd9Sstevel@tonic-gate */ 33057c478bd9Sstevel@tonic-gate 3306*058561cbSjbeck (void) sm_snprintf(SmtpError, sizeof(SmtpError), 33077c478bd9Sstevel@tonic-gate "%s mailer (%s) exited with EX_TEMPFAIL", 33087c478bd9Sstevel@tonic-gate m->m_name, m->m_mailer); 33097c478bd9Sstevel@tonic-gate } 33107c478bd9Sstevel@tonic-gate } 33117c478bd9Sstevel@tonic-gate else 33127c478bd9Sstevel@tonic-gate { 33137c478bd9Sstevel@tonic-gate /* 33147c478bd9Sstevel@tonic-gate ** Send the MAIL FROM: protocol 33157c478bd9Sstevel@tonic-gate */ 33167c478bd9Sstevel@tonic-gate 33177c478bd9Sstevel@tonic-gate /* XXX this isn't pipelined... */ 33187c478bd9Sstevel@tonic-gate rcode = smtpmailfrom(m, mci, e); 33197c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 33207c478bd9Sstevel@tonic-gate { 33217c478bd9Sstevel@tonic-gate register int i; 33227c478bd9Sstevel@tonic-gate # if PIPELINING 33237c478bd9Sstevel@tonic-gate ADDRESS *volatile pchain; 33247c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 33257c478bd9Sstevel@tonic-gate 33267c478bd9Sstevel@tonic-gate /* send the recipient list */ 33277c478bd9Sstevel@tonic-gate tobuf[0] = '\0'; 33287c478bd9Sstevel@tonic-gate mci->mci_retryrcpt = false; 33297c478bd9Sstevel@tonic-gate mci->mci_tolist = tobuf; 33307c478bd9Sstevel@tonic-gate # if PIPELINING 33317c478bd9Sstevel@tonic-gate pchain = NULL; 33327c478bd9Sstevel@tonic-gate mci->mci_nextaddr = NULL; 33337c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 33347c478bd9Sstevel@tonic-gate 33357c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 33367c478bd9Sstevel@tonic-gate { 33377c478bd9Sstevel@tonic-gate if (!QS_IS_UNMARKED(to->q_state)) 33387c478bd9Sstevel@tonic-gate continue; 33397c478bd9Sstevel@tonic-gate 33407c478bd9Sstevel@tonic-gate /* mark recipient state as "ok so far" */ 33417c478bd9Sstevel@tonic-gate to->q_state = QS_OK; 33427c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 33437c478bd9Sstevel@tonic-gate # if STARTTLS 33447c478bd9Sstevel@tonic-gate i = rscheck("tls_rcpt", to->q_user, NULL, e, 33457c478bd9Sstevel@tonic-gate RSF_RMCOMM|RSF_COUNT, 3, 3346*058561cbSjbeck mci->mci_host, e->e_id, NULL); 33477c478bd9Sstevel@tonic-gate if (i != EX_OK) 33487c478bd9Sstevel@tonic-gate { 33497c478bd9Sstevel@tonic-gate markfailure(e, to, mci, i, false); 33507c478bd9Sstevel@tonic-gate giveresponse(i, to->q_status, m, mci, 33517c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 33527c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 33537c478bd9Sstevel@tonic-gate { 33547c478bd9Sstevel@tonic-gate mci->mci_retryrcpt = true; 33557c478bd9Sstevel@tonic-gate to->q_state = QS_RETRY; 33567c478bd9Sstevel@tonic-gate } 33577c478bd9Sstevel@tonic-gate continue; 33587c478bd9Sstevel@tonic-gate } 33597c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 33607c478bd9Sstevel@tonic-gate 33617c478bd9Sstevel@tonic-gate i = smtprcpt(to, m, mci, e, ctladdr, xstart); 33627c478bd9Sstevel@tonic-gate # if PIPELINING 33637c478bd9Sstevel@tonic-gate if (i == EX_OK && 33647c478bd9Sstevel@tonic-gate bitset(MCIF_PIPELINED, mci->mci_flags)) 33657c478bd9Sstevel@tonic-gate { 33667c478bd9Sstevel@tonic-gate /* 33677c478bd9Sstevel@tonic-gate ** Add new element to list of 33687c478bd9Sstevel@tonic-gate ** recipients for pipelining. 33697c478bd9Sstevel@tonic-gate */ 33707c478bd9Sstevel@tonic-gate 33717c478bd9Sstevel@tonic-gate to->q_pchain = NULL; 33727c478bd9Sstevel@tonic-gate if (mci->mci_nextaddr == NULL) 33737c478bd9Sstevel@tonic-gate mci->mci_nextaddr = to; 33747c478bd9Sstevel@tonic-gate if (pchain == NULL) 33757c478bd9Sstevel@tonic-gate pchain = to; 33767c478bd9Sstevel@tonic-gate else 33777c478bd9Sstevel@tonic-gate { 33787c478bd9Sstevel@tonic-gate pchain->q_pchain = to; 33797c478bd9Sstevel@tonic-gate pchain = pchain->q_pchain; 33807c478bd9Sstevel@tonic-gate } 33817c478bd9Sstevel@tonic-gate } 33827c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 33837c478bd9Sstevel@tonic-gate if (i != EX_OK) 33847c478bd9Sstevel@tonic-gate { 33857c478bd9Sstevel@tonic-gate markfailure(e, to, mci, i, false); 33867c478bd9Sstevel@tonic-gate giveresponse(i, to->q_status, m, mci, 33877c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 33887c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 33897c478bd9Sstevel@tonic-gate to->q_state = QS_RETRY; 33907c478bd9Sstevel@tonic-gate } 33917c478bd9Sstevel@tonic-gate } 33927c478bd9Sstevel@tonic-gate 33937c478bd9Sstevel@tonic-gate /* No recipients in list and no missing responses? */ 33947c478bd9Sstevel@tonic-gate if (tobuf[0] == '\0' 33957c478bd9Sstevel@tonic-gate # if PIPELINING 33967c478bd9Sstevel@tonic-gate && mci->mci_nextaddr == NULL 33977c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 33987c478bd9Sstevel@tonic-gate ) 33997c478bd9Sstevel@tonic-gate { 34007c478bd9Sstevel@tonic-gate rcode = EX_OK; 34017c478bd9Sstevel@tonic-gate e->e_to = NULL; 34027c478bd9Sstevel@tonic-gate if (bitset(MCIF_CACHED, mci->mci_flags)) 34037c478bd9Sstevel@tonic-gate smtprset(m, mci, e); 34047c478bd9Sstevel@tonic-gate } 34057c478bd9Sstevel@tonic-gate else 34067c478bd9Sstevel@tonic-gate { 34077c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 34087c478bd9Sstevel@tonic-gate rcode = smtpdata(m, mci, e, ctladdr, xstart); 34097c478bd9Sstevel@tonic-gate } 34107c478bd9Sstevel@tonic-gate } 34117c478bd9Sstevel@tonic-gate if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 34127c478bd9Sstevel@tonic-gate { 34137c478bd9Sstevel@tonic-gate /* try next MX site */ 34147c478bd9Sstevel@tonic-gate goto tryhost; 34157c478bd9Sstevel@tonic-gate } 34167c478bd9Sstevel@tonic-gate } 34177c478bd9Sstevel@tonic-gate #if NAMED_BIND 34187c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 34197c478bd9Sstevel@tonic-gate _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 34207c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 34217c478bd9Sstevel@tonic-gate 34227c478bd9Sstevel@tonic-gate if (tTd(62, 1)) 34237c478bd9Sstevel@tonic-gate checkfds("after delivery"); 34247c478bd9Sstevel@tonic-gate 34257c478bd9Sstevel@tonic-gate /* 34267c478bd9Sstevel@tonic-gate ** Do final status disposal. 34277c478bd9Sstevel@tonic-gate ** We check for something in tobuf for the SMTP case. 34287c478bd9Sstevel@tonic-gate ** If we got a temporary failure, arrange to queue the 34297c478bd9Sstevel@tonic-gate ** addressees. 34307c478bd9Sstevel@tonic-gate */ 34317c478bd9Sstevel@tonic-gate 34327c478bd9Sstevel@tonic-gate give_up: 34337c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 34347c478bd9Sstevel@tonic-gate { 34357c478bd9Sstevel@tonic-gate lmtp_rcode = rcode; 34367c478bd9Sstevel@tonic-gate tobuf[0] = '\0'; 34377c478bd9Sstevel@tonic-gate anyok = false; 34387c478bd9Sstevel@tonic-gate strsize = 0; 34397c478bd9Sstevel@tonic-gate } 34407c478bd9Sstevel@tonic-gate else 34417c478bd9Sstevel@tonic-gate anyok = rcode == EX_OK; 34427c478bd9Sstevel@tonic-gate 34437c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 34447c478bd9Sstevel@tonic-gate { 34457c478bd9Sstevel@tonic-gate /* see if address already marked */ 34467c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 34477c478bd9Sstevel@tonic-gate continue; 34487c478bd9Sstevel@tonic-gate 34497c478bd9Sstevel@tonic-gate /* if running LMTP, get the status for each address */ 34507c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 34517c478bd9Sstevel@tonic-gate { 34527c478bd9Sstevel@tonic-gate if (lmtp_rcode == EX_OK) 34537c478bd9Sstevel@tonic-gate rcode = smtpgetstat(m, mci, e); 34547c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 34557c478bd9Sstevel@tonic-gate { 34567c478bd9Sstevel@tonic-gate strsize += sm_strlcat2(tobuf + strsize, ",", 34577c478bd9Sstevel@tonic-gate to->q_paddr, 34587c478bd9Sstevel@tonic-gate tobufsize - strsize); 34597c478bd9Sstevel@tonic-gate SM_ASSERT(strsize < tobufsize); 34607c478bd9Sstevel@tonic-gate anyok = true; 34617c478bd9Sstevel@tonic-gate } 34627c478bd9Sstevel@tonic-gate else 34637c478bd9Sstevel@tonic-gate { 34647c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 34657c478bd9Sstevel@tonic-gate markfailure(e, to, mci, rcode, true); 34667c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, mci, 34677c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 34687c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 34697c478bd9Sstevel@tonic-gate continue; 34707c478bd9Sstevel@tonic-gate } 34717c478bd9Sstevel@tonic-gate } 34727c478bd9Sstevel@tonic-gate else 34737c478bd9Sstevel@tonic-gate { 34747c478bd9Sstevel@tonic-gate /* mark bad addresses */ 34757c478bd9Sstevel@tonic-gate if (rcode != EX_OK) 34767c478bd9Sstevel@tonic-gate { 34777c478bd9Sstevel@tonic-gate if (goodmxfound && rcode == EX_NOHOST) 34787c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 34797c478bd9Sstevel@tonic-gate markfailure(e, to, mci, rcode, true); 34807c478bd9Sstevel@tonic-gate continue; 34817c478bd9Sstevel@tonic-gate } 34827c478bd9Sstevel@tonic-gate } 34837c478bd9Sstevel@tonic-gate 34847c478bd9Sstevel@tonic-gate /* successful delivery */ 34857c478bd9Sstevel@tonic-gate to->q_state = QS_SENT; 34867c478bd9Sstevel@tonic-gate to->q_statdate = curtime(); 34877c478bd9Sstevel@tonic-gate e->e_nsent++; 34887c478bd9Sstevel@tonic-gate 34897c478bd9Sstevel@tonic-gate /* 34907c478bd9Sstevel@tonic-gate ** Checkpoint the send list every few addresses 34917c478bd9Sstevel@tonic-gate */ 34927c478bd9Sstevel@tonic-gate 34937c478bd9Sstevel@tonic-gate if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 34947c478bd9Sstevel@tonic-gate { 34957c478bd9Sstevel@tonic-gate queueup(e, false, false); 34967c478bd9Sstevel@tonic-gate e->e_nsent = 0; 34977c478bd9Sstevel@tonic-gate } 34987c478bd9Sstevel@tonic-gate 34997c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 35007c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags)) 35017c478bd9Sstevel@tonic-gate { 35027c478bd9Sstevel@tonic-gate to->q_flags |= QDELIVERED; 35037c478bd9Sstevel@tonic-gate to->q_status = "2.1.5"; 35047c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 35057c478bd9Sstevel@tonic-gate "%s... Successfully delivered\n", 35067c478bd9Sstevel@tonic-gate to->q_paddr); 35077c478bd9Sstevel@tonic-gate } 35087c478bd9Sstevel@tonic-gate else if (bitset(QPINGONSUCCESS, to->q_flags) && 35097c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags) && 35107c478bd9Sstevel@tonic-gate !bitset(MCIF_DSN, mci->mci_flags)) 35117c478bd9Sstevel@tonic-gate { 35127c478bd9Sstevel@tonic-gate to->q_flags |= QRELAYED; 35137c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 35147c478bd9Sstevel@tonic-gate "%s... relayed; expect no further notifications\n", 35157c478bd9Sstevel@tonic-gate to->q_paddr); 35167c478bd9Sstevel@tonic-gate } 35177c478bd9Sstevel@tonic-gate else if (IS_DLVR_NOTIFY(e) && 35187c478bd9Sstevel@tonic-gate !bitset(MCIF_DLVR_BY, mci->mci_flags) && 35197c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags) && 35207c478bd9Sstevel@tonic-gate (!bitset(QHASNOTIFY, to->q_flags) || 35217c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags) || 35227c478bd9Sstevel@tonic-gate bitset(QPINGONFAILURE, to->q_flags) || 35237c478bd9Sstevel@tonic-gate bitset(QPINGONDELAY, to->q_flags))) 35247c478bd9Sstevel@tonic-gate { 35257c478bd9Sstevel@tonic-gate /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 35267c478bd9Sstevel@tonic-gate to->q_flags |= QBYNRELAY; 35277c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 35287c478bd9Sstevel@tonic-gate "%s... Deliver-by notify: relayed\n", 35297c478bd9Sstevel@tonic-gate to->q_paddr); 35307c478bd9Sstevel@tonic-gate } 35317c478bd9Sstevel@tonic-gate else if (IS_DLVR_TRACE(e) && 35327c478bd9Sstevel@tonic-gate (!bitset(QHASNOTIFY, to->q_flags) || 35337c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags) || 35347c478bd9Sstevel@tonic-gate bitset(QPINGONFAILURE, to->q_flags) || 35357c478bd9Sstevel@tonic-gate bitset(QPINGONDELAY, to->q_flags)) && 35367c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags)) 35377c478bd9Sstevel@tonic-gate { 35387c478bd9Sstevel@tonic-gate /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 35397c478bd9Sstevel@tonic-gate to->q_flags |= QBYTRACE; 35407c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 35417c478bd9Sstevel@tonic-gate "%s... Deliver-By trace: relayed\n", 35427c478bd9Sstevel@tonic-gate to->q_paddr); 35437c478bd9Sstevel@tonic-gate } 35447c478bd9Sstevel@tonic-gate } 35457c478bd9Sstevel@tonic-gate 35467c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 35477c478bd9Sstevel@tonic-gate { 35487c478bd9Sstevel@tonic-gate /* 35497c478bd9Sstevel@tonic-gate ** Global information applies to the last recipient only; 35507c478bd9Sstevel@tonic-gate ** clear it out to avoid bogus errors. 35517c478bd9Sstevel@tonic-gate */ 35527c478bd9Sstevel@tonic-gate 35537c478bd9Sstevel@tonic-gate rcode = EX_OK; 35547c478bd9Sstevel@tonic-gate e->e_statmsg = NULL; 35557c478bd9Sstevel@tonic-gate 35567c478bd9Sstevel@tonic-gate /* reset the mci state for the next transaction */ 35577c478bd9Sstevel@tonic-gate if (mci != NULL && 35587c478bd9Sstevel@tonic-gate (mci->mci_state == MCIS_MAIL || 35597c478bd9Sstevel@tonic-gate mci->mci_state == MCIS_RCPT || 35607c478bd9Sstevel@tonic-gate mci->mci_state == MCIS_DATA)) 35617c478bd9Sstevel@tonic-gate { 35627c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 35637c478bd9Sstevel@tonic-gate SmtpPhase = mci->mci_phase = "idle"; 35647c478bd9Sstevel@tonic-gate sm_setproctitle(true, e, "%s: %s", CurHostName, 35657c478bd9Sstevel@tonic-gate mci->mci_phase); 35667c478bd9Sstevel@tonic-gate } 35677c478bd9Sstevel@tonic-gate } 35687c478bd9Sstevel@tonic-gate 35697c478bd9Sstevel@tonic-gate if (tobuf[0] != '\0') 35707c478bd9Sstevel@tonic-gate { 35717c478bd9Sstevel@tonic-gate giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 35727c478bd9Sstevel@tonic-gate #if 0 35737c478bd9Sstevel@tonic-gate /* 35747c478bd9Sstevel@tonic-gate ** This code is disabled for now because I am not 35757c478bd9Sstevel@tonic-gate ** sure that copying status from the first recipient 35767c478bd9Sstevel@tonic-gate ** to all non-status'ed recipients is a good idea. 35777c478bd9Sstevel@tonic-gate */ 35787c478bd9Sstevel@tonic-gate 35797c478bd9Sstevel@tonic-gate if (tochain->q_message != NULL && 35807c478bd9Sstevel@tonic-gate !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 35817c478bd9Sstevel@tonic-gate { 35827c478bd9Sstevel@tonic-gate for (to = tochain->q_tchain; to != NULL; 35837c478bd9Sstevel@tonic-gate to = to->q_tchain) 35847c478bd9Sstevel@tonic-gate { 35857c478bd9Sstevel@tonic-gate /* see if address already marked */ 35867c478bd9Sstevel@tonic-gate if (QS_IS_QUEUEUP(to->q_state) && 35877c478bd9Sstevel@tonic-gate to->q_message == NULL) 35887c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 35897c478bd9Sstevel@tonic-gate tochain->q_message); 35907c478bd9Sstevel@tonic-gate } 35917c478bd9Sstevel@tonic-gate } 35927c478bd9Sstevel@tonic-gate #endif /* 0 */ 35937c478bd9Sstevel@tonic-gate } 35947c478bd9Sstevel@tonic-gate if (anyok) 35957c478bd9Sstevel@tonic-gate markstats(e, tochain, STATS_NORMAL); 35967c478bd9Sstevel@tonic-gate mci_store_persistent(mci); 35977c478bd9Sstevel@tonic-gate 35987c478bd9Sstevel@tonic-gate /* Some recipients were tempfailed, try them on the next host */ 35997c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 36007c478bd9Sstevel@tonic-gate { 36017c478bd9Sstevel@tonic-gate /* try next MX site */ 36027c478bd9Sstevel@tonic-gate goto tryhost; 36037c478bd9Sstevel@tonic-gate } 36047c478bd9Sstevel@tonic-gate 36057c478bd9Sstevel@tonic-gate /* now close the connection */ 36067c478bd9Sstevel@tonic-gate if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 36077c478bd9Sstevel@tonic-gate !bitset(MCIF_CACHED, mci->mci_flags)) 36087c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 36097c478bd9Sstevel@tonic-gate 36107c478bd9Sstevel@tonic-gate cleanup: ; 36117c478bd9Sstevel@tonic-gate } 36127c478bd9Sstevel@tonic-gate SM_FINALLY 36137c478bd9Sstevel@tonic-gate { 36147c478bd9Sstevel@tonic-gate /* 36157c478bd9Sstevel@tonic-gate ** Restore state and return. 36167c478bd9Sstevel@tonic-gate */ 36177c478bd9Sstevel@tonic-gate #if XDEBUG 36187c478bd9Sstevel@tonic-gate char wbuf[MAXLINE]; 36197c478bd9Sstevel@tonic-gate 36207c478bd9Sstevel@tonic-gate /* make absolutely certain 0, 1, and 2 are in use */ 3621*058561cbSjbeck (void) sm_snprintf(wbuf, sizeof(wbuf), 36227c478bd9Sstevel@tonic-gate "%s... end of deliver(%s)", 36237c478bd9Sstevel@tonic-gate e->e_to == NULL ? "NO-TO-LIST" 36247c478bd9Sstevel@tonic-gate : shortenstring(e->e_to, 36257c478bd9Sstevel@tonic-gate MAXSHORTSTR), 36267c478bd9Sstevel@tonic-gate m->m_name); 36277c478bd9Sstevel@tonic-gate checkfd012(wbuf); 36287c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 36297c478bd9Sstevel@tonic-gate 36307c478bd9Sstevel@tonic-gate errno = 0; 36317c478bd9Sstevel@tonic-gate 36327c478bd9Sstevel@tonic-gate /* 36337c478bd9Sstevel@tonic-gate ** It was originally necessary to set macro 'g' to NULL 36347c478bd9Sstevel@tonic-gate ** because it previously pointed to an auto buffer. 36357c478bd9Sstevel@tonic-gate ** We don't do this any more, so this may be unnecessary. 36367c478bd9Sstevel@tonic-gate */ 36377c478bd9Sstevel@tonic-gate 36387c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 36397c478bd9Sstevel@tonic-gate e->e_to = NULL; 36407c478bd9Sstevel@tonic-gate } 36417c478bd9Sstevel@tonic-gate SM_END_TRY 36427c478bd9Sstevel@tonic-gate return rcode; 36437c478bd9Sstevel@tonic-gate } 36447c478bd9Sstevel@tonic-gate 36457c478bd9Sstevel@tonic-gate /* 36467c478bd9Sstevel@tonic-gate ** MARKFAILURE -- mark a failure on a specific address. 36477c478bd9Sstevel@tonic-gate ** 36487c478bd9Sstevel@tonic-gate ** Parameters: 36497c478bd9Sstevel@tonic-gate ** e -- the envelope we are sending. 36507c478bd9Sstevel@tonic-gate ** q -- the address to mark. 36517c478bd9Sstevel@tonic-gate ** mci -- mailer connection information. 36527c478bd9Sstevel@tonic-gate ** rcode -- the code signifying the particular failure. 36537c478bd9Sstevel@tonic-gate ** ovr -- override an existing code? 36547c478bd9Sstevel@tonic-gate ** 36557c478bd9Sstevel@tonic-gate ** Returns: 36567c478bd9Sstevel@tonic-gate ** none. 36577c478bd9Sstevel@tonic-gate ** 36587c478bd9Sstevel@tonic-gate ** Side Effects: 36597c478bd9Sstevel@tonic-gate ** marks the address (and possibly the envelope) with the 36607c478bd9Sstevel@tonic-gate ** failure so that an error will be returned or 36617c478bd9Sstevel@tonic-gate ** the message will be queued, as appropriate. 36627c478bd9Sstevel@tonic-gate */ 36637c478bd9Sstevel@tonic-gate 36647c478bd9Sstevel@tonic-gate void 36657c478bd9Sstevel@tonic-gate markfailure(e, q, mci, rcode, ovr) 36667c478bd9Sstevel@tonic-gate register ENVELOPE *e; 36677c478bd9Sstevel@tonic-gate register ADDRESS *q; 36687c478bd9Sstevel@tonic-gate register MCI *mci; 36697c478bd9Sstevel@tonic-gate int rcode; 36707c478bd9Sstevel@tonic-gate bool ovr; 36717c478bd9Sstevel@tonic-gate { 36727c478bd9Sstevel@tonic-gate int save_errno = errno; 36737c478bd9Sstevel@tonic-gate char *status = NULL; 36747c478bd9Sstevel@tonic-gate char *rstatus = NULL; 36757c478bd9Sstevel@tonic-gate 36767c478bd9Sstevel@tonic-gate switch (rcode) 36777c478bd9Sstevel@tonic-gate { 36787c478bd9Sstevel@tonic-gate case EX_OK: 36797c478bd9Sstevel@tonic-gate break; 36807c478bd9Sstevel@tonic-gate 36817c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 36827c478bd9Sstevel@tonic-gate case EX_IOERR: 36837c478bd9Sstevel@tonic-gate case EX_OSERR: 36847c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 36857c478bd9Sstevel@tonic-gate break; 36867c478bd9Sstevel@tonic-gate 36877c478bd9Sstevel@tonic-gate default: 36887c478bd9Sstevel@tonic-gate q->q_state = QS_BADADDR; 36897c478bd9Sstevel@tonic-gate break; 36907c478bd9Sstevel@tonic-gate } 36917c478bd9Sstevel@tonic-gate 36927c478bd9Sstevel@tonic-gate /* find most specific error code possible */ 36937c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_status != NULL) 36947c478bd9Sstevel@tonic-gate { 36957c478bd9Sstevel@tonic-gate status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 36967c478bd9Sstevel@tonic-gate if (mci->mci_rstatus != NULL) 36977c478bd9Sstevel@tonic-gate rstatus = sm_rpool_strdup_x(e->e_rpool, 36987c478bd9Sstevel@tonic-gate mci->mci_rstatus); 36997c478bd9Sstevel@tonic-gate else 37007c478bd9Sstevel@tonic-gate rstatus = NULL; 37017c478bd9Sstevel@tonic-gate } 37027c478bd9Sstevel@tonic-gate else if (e->e_status != NULL) 37037c478bd9Sstevel@tonic-gate { 37047c478bd9Sstevel@tonic-gate status = e->e_status; 37057c478bd9Sstevel@tonic-gate rstatus = NULL; 37067c478bd9Sstevel@tonic-gate } 37077c478bd9Sstevel@tonic-gate else 37087c478bd9Sstevel@tonic-gate { 37097c478bd9Sstevel@tonic-gate switch (rcode) 37107c478bd9Sstevel@tonic-gate { 37117c478bd9Sstevel@tonic-gate case EX_USAGE: 37127c478bd9Sstevel@tonic-gate status = "5.5.4"; 37137c478bd9Sstevel@tonic-gate break; 37147c478bd9Sstevel@tonic-gate 37157c478bd9Sstevel@tonic-gate case EX_DATAERR: 37167c478bd9Sstevel@tonic-gate status = "5.5.2"; 37177c478bd9Sstevel@tonic-gate break; 37187c478bd9Sstevel@tonic-gate 37197c478bd9Sstevel@tonic-gate case EX_NOUSER: 37207c478bd9Sstevel@tonic-gate status = "5.1.1"; 37217c478bd9Sstevel@tonic-gate break; 37227c478bd9Sstevel@tonic-gate 37237c478bd9Sstevel@tonic-gate case EX_NOHOST: 37247c478bd9Sstevel@tonic-gate status = "5.1.2"; 37257c478bd9Sstevel@tonic-gate break; 37267c478bd9Sstevel@tonic-gate 37277c478bd9Sstevel@tonic-gate case EX_NOINPUT: 37287c478bd9Sstevel@tonic-gate case EX_CANTCREAT: 37297c478bd9Sstevel@tonic-gate case EX_NOPERM: 37307c478bd9Sstevel@tonic-gate status = "5.3.0"; 37317c478bd9Sstevel@tonic-gate break; 37327c478bd9Sstevel@tonic-gate 37337c478bd9Sstevel@tonic-gate case EX_UNAVAILABLE: 37347c478bd9Sstevel@tonic-gate case EX_SOFTWARE: 37357c478bd9Sstevel@tonic-gate case EX_OSFILE: 37367c478bd9Sstevel@tonic-gate case EX_PROTOCOL: 37377c478bd9Sstevel@tonic-gate case EX_CONFIG: 37387c478bd9Sstevel@tonic-gate status = "5.5.0"; 37397c478bd9Sstevel@tonic-gate break; 37407c478bd9Sstevel@tonic-gate 37417c478bd9Sstevel@tonic-gate case EX_OSERR: 37427c478bd9Sstevel@tonic-gate case EX_IOERR: 37437c478bd9Sstevel@tonic-gate status = "4.5.0"; 37447c478bd9Sstevel@tonic-gate break; 37457c478bd9Sstevel@tonic-gate 37467c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 37477c478bd9Sstevel@tonic-gate status = "4.2.0"; 37487c478bd9Sstevel@tonic-gate break; 37497c478bd9Sstevel@tonic-gate } 37507c478bd9Sstevel@tonic-gate } 37517c478bd9Sstevel@tonic-gate 37527c478bd9Sstevel@tonic-gate /* new status? */ 37537c478bd9Sstevel@tonic-gate if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 37547c478bd9Sstevel@tonic-gate *q->q_status == '\0' || *q->q_status < *status)) 37557c478bd9Sstevel@tonic-gate { 37567c478bd9Sstevel@tonic-gate q->q_status = status; 37577c478bd9Sstevel@tonic-gate q->q_rstatus = rstatus; 37587c478bd9Sstevel@tonic-gate } 37597c478bd9Sstevel@tonic-gate if (rcode != EX_OK && q->q_rstatus == NULL && 37607c478bd9Sstevel@tonic-gate q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 37617c478bd9Sstevel@tonic-gate sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 37627c478bd9Sstevel@tonic-gate { 37637c478bd9Sstevel@tonic-gate char buf[16]; 37647c478bd9Sstevel@tonic-gate 3765*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "%d", rcode); 37667c478bd9Sstevel@tonic-gate q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 37677c478bd9Sstevel@tonic-gate } 37687c478bd9Sstevel@tonic-gate 37697c478bd9Sstevel@tonic-gate q->q_statdate = curtime(); 37707c478bd9Sstevel@tonic-gate if (CurHostName != NULL && CurHostName[0] != '\0' && 37717c478bd9Sstevel@tonic-gate mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 37727c478bd9Sstevel@tonic-gate q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 37737c478bd9Sstevel@tonic-gate 37747c478bd9Sstevel@tonic-gate /* restore errno */ 37757c478bd9Sstevel@tonic-gate errno = save_errno; 37767c478bd9Sstevel@tonic-gate } 37777c478bd9Sstevel@tonic-gate /* 37787c478bd9Sstevel@tonic-gate ** ENDMAILER -- Wait for mailer to terminate. 37797c478bd9Sstevel@tonic-gate ** 37807c478bd9Sstevel@tonic-gate ** We should never get fatal errors (e.g., segmentation 37817c478bd9Sstevel@tonic-gate ** violation), so we report those specially. For other 37827c478bd9Sstevel@tonic-gate ** errors, we choose a status message (into statmsg), 37837c478bd9Sstevel@tonic-gate ** and if it represents an error, we print it. 37847c478bd9Sstevel@tonic-gate ** 37857c478bd9Sstevel@tonic-gate ** Parameters: 37867c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 37877c478bd9Sstevel@tonic-gate ** e -- the current envelope. 37887c478bd9Sstevel@tonic-gate ** pv -- the parameter vector that invoked the mailer 37897c478bd9Sstevel@tonic-gate ** (for error messages). 37907c478bd9Sstevel@tonic-gate ** 37917c478bd9Sstevel@tonic-gate ** Returns: 37927c478bd9Sstevel@tonic-gate ** exit code of mailer. 37937c478bd9Sstevel@tonic-gate ** 37947c478bd9Sstevel@tonic-gate ** Side Effects: 37957c478bd9Sstevel@tonic-gate ** none. 37967c478bd9Sstevel@tonic-gate */ 37977c478bd9Sstevel@tonic-gate 37987c478bd9Sstevel@tonic-gate static jmp_buf EndWaitTimeout; 37997c478bd9Sstevel@tonic-gate 38007c478bd9Sstevel@tonic-gate static void 38017c478bd9Sstevel@tonic-gate endwaittimeout(ignore) 38027c478bd9Sstevel@tonic-gate int ignore; 38037c478bd9Sstevel@tonic-gate { 38047c478bd9Sstevel@tonic-gate /* 38057c478bd9Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 38067c478bd9Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 38077c478bd9Sstevel@tonic-gate ** DOING. 38087c478bd9Sstevel@tonic-gate */ 38097c478bd9Sstevel@tonic-gate 38107c478bd9Sstevel@tonic-gate errno = ETIMEDOUT; 38117c478bd9Sstevel@tonic-gate longjmp(EndWaitTimeout, 1); 38127c478bd9Sstevel@tonic-gate } 38137c478bd9Sstevel@tonic-gate 38147c478bd9Sstevel@tonic-gate int 38157c478bd9Sstevel@tonic-gate endmailer(mci, e, pv) 38167c478bd9Sstevel@tonic-gate register MCI *mci; 38177c478bd9Sstevel@tonic-gate register ENVELOPE *e; 38187c478bd9Sstevel@tonic-gate char **pv; 38197c478bd9Sstevel@tonic-gate { 38207c478bd9Sstevel@tonic-gate int st; 38217c478bd9Sstevel@tonic-gate int save_errno = errno; 38227c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 38237c478bd9Sstevel@tonic-gate SM_EVENT *ev = NULL; 38247c478bd9Sstevel@tonic-gate 38257c478bd9Sstevel@tonic-gate 38267c478bd9Sstevel@tonic-gate mci_unlock_host(mci); 38277c478bd9Sstevel@tonic-gate 38287c478bd9Sstevel@tonic-gate /* close output to mailer */ 38297c478bd9Sstevel@tonic-gate if (mci->mci_out != NULL) 38307c478bd9Sstevel@tonic-gate { 38317c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 38327c478bd9Sstevel@tonic-gate mci->mci_out = NULL; 38337c478bd9Sstevel@tonic-gate } 38347c478bd9Sstevel@tonic-gate 38357c478bd9Sstevel@tonic-gate /* copy any remaining input to transcript */ 38367c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 38377c478bd9Sstevel@tonic-gate e->e_xfp != NULL) 38387c478bd9Sstevel@tonic-gate { 3839*058561cbSjbeck while (sfgets(buf, sizeof(buf), mci->mci_in, 38407c478bd9Sstevel@tonic-gate TimeOuts.to_quit, "Draining Input") != NULL) 38417c478bd9Sstevel@tonic-gate (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 38427c478bd9Sstevel@tonic-gate } 38437c478bd9Sstevel@tonic-gate 38447c478bd9Sstevel@tonic-gate #if SASL 38457c478bd9Sstevel@tonic-gate /* close SASL connection */ 38467c478bd9Sstevel@tonic-gate if (bitset(MCIF_AUTHACT, mci->mci_flags)) 38477c478bd9Sstevel@tonic-gate { 38487c478bd9Sstevel@tonic-gate sasl_dispose(&mci->mci_conn); 38497c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_AUTHACT; 38507c478bd9Sstevel@tonic-gate } 38517c478bd9Sstevel@tonic-gate #endif /* SASL */ 38527c478bd9Sstevel@tonic-gate 38537c478bd9Sstevel@tonic-gate #if STARTTLS 38547c478bd9Sstevel@tonic-gate /* shutdown TLS */ 38557c478bd9Sstevel@tonic-gate (void) endtlsclt(mci); 38567c478bd9Sstevel@tonic-gate #endif /* STARTTLS */ 38577c478bd9Sstevel@tonic-gate 38587c478bd9Sstevel@tonic-gate /* now close the input */ 38597c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL) 38607c478bd9Sstevel@tonic-gate { 38617c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 38627c478bd9Sstevel@tonic-gate mci->mci_in = NULL; 38637c478bd9Sstevel@tonic-gate } 38647c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_CLOSED; 38657c478bd9Sstevel@tonic-gate 38667c478bd9Sstevel@tonic-gate errno = save_errno; 38677c478bd9Sstevel@tonic-gate 38687c478bd9Sstevel@tonic-gate /* in the IPC case there is nothing to wait for */ 38697c478bd9Sstevel@tonic-gate if (mci->mci_pid == 0) 38707c478bd9Sstevel@tonic-gate return EX_OK; 38717c478bd9Sstevel@tonic-gate 38727c478bd9Sstevel@tonic-gate /* put a timeout around the wait */ 38737c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_wait > 0) 38747c478bd9Sstevel@tonic-gate { 38757c478bd9Sstevel@tonic-gate if (setjmp(EndWaitTimeout) == 0) 38767c478bd9Sstevel@tonic-gate ev = sm_setevent(mci->mci_mailer->m_wait, 38777c478bd9Sstevel@tonic-gate endwaittimeout, 0); 38787c478bd9Sstevel@tonic-gate else 38797c478bd9Sstevel@tonic-gate { 38807c478bd9Sstevel@tonic-gate syserr("endmailer %s: wait timeout (%ld)", 38817c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name, 38827c478bd9Sstevel@tonic-gate (long) mci->mci_mailer->m_wait); 38837c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 38847c478bd9Sstevel@tonic-gate } 38857c478bd9Sstevel@tonic-gate } 38867c478bd9Sstevel@tonic-gate 38877c478bd9Sstevel@tonic-gate /* wait for the mailer process, collect status */ 38887c478bd9Sstevel@tonic-gate st = waitfor(mci->mci_pid); 38897c478bd9Sstevel@tonic-gate save_errno = errno; 38907c478bd9Sstevel@tonic-gate if (ev != NULL) 38917c478bd9Sstevel@tonic-gate sm_clrevent(ev); 38927c478bd9Sstevel@tonic-gate errno = save_errno; 38937c478bd9Sstevel@tonic-gate 38947c478bd9Sstevel@tonic-gate if (st == -1) 38957c478bd9Sstevel@tonic-gate { 38967c478bd9Sstevel@tonic-gate syserr("endmailer %s: wait", mci->mci_mailer->m_name); 38977c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 38987c478bd9Sstevel@tonic-gate } 38997c478bd9Sstevel@tonic-gate 39007c478bd9Sstevel@tonic-gate if (WIFEXITED(st)) 39017c478bd9Sstevel@tonic-gate { 39027c478bd9Sstevel@tonic-gate /* normal death -- return status */ 39037c478bd9Sstevel@tonic-gate return (WEXITSTATUS(st)); 39047c478bd9Sstevel@tonic-gate } 39057c478bd9Sstevel@tonic-gate 39067c478bd9Sstevel@tonic-gate /* it died a horrid death */ 39077c478bd9Sstevel@tonic-gate syserr("451 4.3.0 mailer %s died with signal %d%s", 39087c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name, WTERMSIG(st), 39097c478bd9Sstevel@tonic-gate WCOREDUMP(st) ? " (core dumped)" : 39107c478bd9Sstevel@tonic-gate (WIFSTOPPED(st) ? " (stopped)" : "")); 39117c478bd9Sstevel@tonic-gate 39127c478bd9Sstevel@tonic-gate /* log the arguments */ 39137c478bd9Sstevel@tonic-gate if (pv != NULL && e->e_xfp != NULL) 39147c478bd9Sstevel@tonic-gate { 39157c478bd9Sstevel@tonic-gate register char **av; 39167c478bd9Sstevel@tonic-gate 39177c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 39187c478bd9Sstevel@tonic-gate for (av = pv; *av != NULL; av++) 39197c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 39207c478bd9Sstevel@tonic-gate *av); 39217c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 39227c478bd9Sstevel@tonic-gate } 39237c478bd9Sstevel@tonic-gate 39247c478bd9Sstevel@tonic-gate ExitStat = EX_TEMPFAIL; 39257c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 39267c478bd9Sstevel@tonic-gate } 39277c478bd9Sstevel@tonic-gate /* 39287c478bd9Sstevel@tonic-gate ** GIVERESPONSE -- Interpret an error response from a mailer 39297c478bd9Sstevel@tonic-gate ** 39307c478bd9Sstevel@tonic-gate ** Parameters: 39317c478bd9Sstevel@tonic-gate ** status -- the status code from the mailer (high byte 39327c478bd9Sstevel@tonic-gate ** only; core dumps must have been taken care of 39337c478bd9Sstevel@tonic-gate ** already). 39347c478bd9Sstevel@tonic-gate ** dsn -- the DSN associated with the address, if any. 39357c478bd9Sstevel@tonic-gate ** m -- the mailer info for this mailer. 39367c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info -- can be NULL if the 39377c478bd9Sstevel@tonic-gate ** response is given before the connection is made. 39387c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address for the recipient 39397c478bd9Sstevel@tonic-gate ** address(es). 39407c478bd9Sstevel@tonic-gate ** xstart -- the transaction start time, for computing 39417c478bd9Sstevel@tonic-gate ** transaction delays. 39427c478bd9Sstevel@tonic-gate ** e -- the current envelope. 39437c478bd9Sstevel@tonic-gate ** to -- the current recipient (NULL if none). 39447c478bd9Sstevel@tonic-gate ** 39457c478bd9Sstevel@tonic-gate ** Returns: 39467c478bd9Sstevel@tonic-gate ** none. 39477c478bd9Sstevel@tonic-gate ** 39487c478bd9Sstevel@tonic-gate ** Side Effects: 39497c478bd9Sstevel@tonic-gate ** Errors may be incremented. 39507c478bd9Sstevel@tonic-gate ** ExitStat may be set. 39517c478bd9Sstevel@tonic-gate */ 39527c478bd9Sstevel@tonic-gate 39537c478bd9Sstevel@tonic-gate void 39547c478bd9Sstevel@tonic-gate giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 39557c478bd9Sstevel@tonic-gate int status; 39567c478bd9Sstevel@tonic-gate char *dsn; 39577c478bd9Sstevel@tonic-gate register MAILER *m; 39587c478bd9Sstevel@tonic-gate register MCI *mci; 39597c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 39607c478bd9Sstevel@tonic-gate time_t xstart; 39617c478bd9Sstevel@tonic-gate ENVELOPE *e; 39627c478bd9Sstevel@tonic-gate ADDRESS *to; 39637c478bd9Sstevel@tonic-gate { 39647c478bd9Sstevel@tonic-gate register const char *statmsg; 39657c478bd9Sstevel@tonic-gate int errnum = errno; 39667c478bd9Sstevel@tonic-gate int off = 4; 39677c478bd9Sstevel@tonic-gate bool usestat = false; 39687c478bd9Sstevel@tonic-gate char dsnbuf[ENHSCLEN]; 39697c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 39707c478bd9Sstevel@tonic-gate char *exmsg; 39717c478bd9Sstevel@tonic-gate 39727c478bd9Sstevel@tonic-gate if (e == NULL) 39733ee0e492Sjbeck { 39747c478bd9Sstevel@tonic-gate syserr("giveresponse: null envelope"); 39753ee0e492Sjbeck /* NOTREACHED */ 39763ee0e492Sjbeck SM_ASSERT(0); 39773ee0e492Sjbeck } 39787c478bd9Sstevel@tonic-gate 39797c478bd9Sstevel@tonic-gate /* 39807c478bd9Sstevel@tonic-gate ** Compute status message from code. 39817c478bd9Sstevel@tonic-gate */ 39827c478bd9Sstevel@tonic-gate 39837c478bd9Sstevel@tonic-gate exmsg = sm_sysexmsg(status); 39847c478bd9Sstevel@tonic-gate if (status == 0) 39857c478bd9Sstevel@tonic-gate { 39867c478bd9Sstevel@tonic-gate statmsg = "250 2.0.0 Sent"; 39877c478bd9Sstevel@tonic-gate if (e->e_statmsg != NULL) 39887c478bd9Sstevel@tonic-gate { 3989*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", 39907c478bd9Sstevel@tonic-gate statmsg, 39917c478bd9Sstevel@tonic-gate shortenstring(e->e_statmsg, 403)); 39927c478bd9Sstevel@tonic-gate statmsg = buf; 39937c478bd9Sstevel@tonic-gate } 39947c478bd9Sstevel@tonic-gate } 39957c478bd9Sstevel@tonic-gate else if (exmsg == NULL) 39967c478bd9Sstevel@tonic-gate { 3997*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), 39987c478bd9Sstevel@tonic-gate "554 5.3.0 unknown mailer error %d", 39997c478bd9Sstevel@tonic-gate status); 40007c478bd9Sstevel@tonic-gate status = EX_UNAVAILABLE; 40017c478bd9Sstevel@tonic-gate statmsg = buf; 40027c478bd9Sstevel@tonic-gate usestat = true; 40037c478bd9Sstevel@tonic-gate } 40047c478bd9Sstevel@tonic-gate else if (status == EX_TEMPFAIL) 40057c478bd9Sstevel@tonic-gate { 40067c478bd9Sstevel@tonic-gate char *bp = buf; 40077c478bd9Sstevel@tonic-gate 40087c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 40097c478bd9Sstevel@tonic-gate bp += strlen(bp); 40107c478bd9Sstevel@tonic-gate #if NAMED_BIND 40117c478bd9Sstevel@tonic-gate if (h_errno == TRY_AGAIN) 40127c478bd9Sstevel@tonic-gate statmsg = sm_errstring(h_errno + E_DNSBASE); 40137c478bd9Sstevel@tonic-gate else 40147c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 40157c478bd9Sstevel@tonic-gate { 40167c478bd9Sstevel@tonic-gate if (errnum != 0) 40177c478bd9Sstevel@tonic-gate statmsg = sm_errstring(errnum); 40187c478bd9Sstevel@tonic-gate else 40197c478bd9Sstevel@tonic-gate statmsg = SmtpError; 40207c478bd9Sstevel@tonic-gate } 40217c478bd9Sstevel@tonic-gate if (statmsg != NULL && statmsg[0] != '\0') 40227c478bd9Sstevel@tonic-gate { 40237c478bd9Sstevel@tonic-gate switch (errnum) 40247c478bd9Sstevel@tonic-gate { 40257c478bd9Sstevel@tonic-gate #ifdef ENETDOWN 40267c478bd9Sstevel@tonic-gate case ENETDOWN: /* Network is down */ 40277c478bd9Sstevel@tonic-gate #endif /* ENETDOWN */ 40287c478bd9Sstevel@tonic-gate #ifdef ENETUNREACH 40297c478bd9Sstevel@tonic-gate case ENETUNREACH: /* Network is unreachable */ 40307c478bd9Sstevel@tonic-gate #endif /* ENETUNREACH */ 40317c478bd9Sstevel@tonic-gate #ifdef ENETRESET 40327c478bd9Sstevel@tonic-gate case ENETRESET: /* Network dropped connection on reset */ 40337c478bd9Sstevel@tonic-gate #endif /* ENETRESET */ 40347c478bd9Sstevel@tonic-gate #ifdef ECONNABORTED 40357c478bd9Sstevel@tonic-gate case ECONNABORTED: /* Software caused connection abort */ 40367c478bd9Sstevel@tonic-gate #endif /* ECONNABORTED */ 40377c478bd9Sstevel@tonic-gate #ifdef EHOSTDOWN 40387c478bd9Sstevel@tonic-gate case EHOSTDOWN: /* Host is down */ 40397c478bd9Sstevel@tonic-gate #endif /* EHOSTDOWN */ 40407c478bd9Sstevel@tonic-gate #ifdef EHOSTUNREACH 40417c478bd9Sstevel@tonic-gate case EHOSTUNREACH: /* No route to host */ 40427c478bd9Sstevel@tonic-gate #endif /* EHOSTUNREACH */ 40437c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 40447c478bd9Sstevel@tonic-gate { 40457c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, 40467c478bd9Sstevel@tonic-gate SPACELEFT(buf, bp), 40477c478bd9Sstevel@tonic-gate 2, ": ", 40487c478bd9Sstevel@tonic-gate mci->mci_host); 40497c478bd9Sstevel@tonic-gate bp += strlen(bp); 40507c478bd9Sstevel@tonic-gate } 40517c478bd9Sstevel@tonic-gate break; 40527c478bd9Sstevel@tonic-gate } 40537c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 40547c478bd9Sstevel@tonic-gate statmsg); 40557c478bd9Sstevel@tonic-gate usestat = true; 40567c478bd9Sstevel@tonic-gate } 40577c478bd9Sstevel@tonic-gate statmsg = buf; 40587c478bd9Sstevel@tonic-gate } 40597c478bd9Sstevel@tonic-gate #if NAMED_BIND 40607c478bd9Sstevel@tonic-gate else if (status == EX_NOHOST && h_errno != 0) 40617c478bd9Sstevel@tonic-gate { 40627c478bd9Sstevel@tonic-gate statmsg = sm_errstring(h_errno + E_DNSBASE); 4063*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", exmsg + 1, 40647c478bd9Sstevel@tonic-gate statmsg); 40657c478bd9Sstevel@tonic-gate statmsg = buf; 40667c478bd9Sstevel@tonic-gate usestat = true; 40677c478bd9Sstevel@tonic-gate } 40687c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 40697c478bd9Sstevel@tonic-gate else 40707c478bd9Sstevel@tonic-gate { 40717c478bd9Sstevel@tonic-gate statmsg = exmsg; 40727c478bd9Sstevel@tonic-gate if (*statmsg++ == ':' && errnum != 0) 40737c478bd9Sstevel@tonic-gate { 4074*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "%s: %s", statmsg, 40757c478bd9Sstevel@tonic-gate sm_errstring(errnum)); 40767c478bd9Sstevel@tonic-gate statmsg = buf; 40777c478bd9Sstevel@tonic-gate usestat = true; 40787c478bd9Sstevel@tonic-gate } 40797c478bd9Sstevel@tonic-gate else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 40807c478bd9Sstevel@tonic-gate { 4081*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "%s (%s)", statmsg, 40827c478bd9Sstevel@tonic-gate shortenstring(e->e_statmsg, 403)); 40837c478bd9Sstevel@tonic-gate statmsg = buf; 40847c478bd9Sstevel@tonic-gate usestat = true; 40857c478bd9Sstevel@tonic-gate } 40867c478bd9Sstevel@tonic-gate } 40877c478bd9Sstevel@tonic-gate 40887c478bd9Sstevel@tonic-gate /* 40897c478bd9Sstevel@tonic-gate ** Print the message as appropriate 40907c478bd9Sstevel@tonic-gate */ 40917c478bd9Sstevel@tonic-gate 40927c478bd9Sstevel@tonic-gate if (status == EX_OK || status == EX_TEMPFAIL) 40937c478bd9Sstevel@tonic-gate { 40947c478bd9Sstevel@tonic-gate extern char MsgBuf[]; 40957c478bd9Sstevel@tonic-gate 40967c478bd9Sstevel@tonic-gate if ((off = isenhsc(statmsg + 4, ' ')) > 0) 40977c478bd9Sstevel@tonic-gate { 40987c478bd9Sstevel@tonic-gate if (dsn == NULL) 40997c478bd9Sstevel@tonic-gate { 4100*058561cbSjbeck (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 41017c478bd9Sstevel@tonic-gate "%.*s", off, statmsg + 4); 41027c478bd9Sstevel@tonic-gate dsn = dsnbuf; 41037c478bd9Sstevel@tonic-gate } 41047c478bd9Sstevel@tonic-gate off += 5; 41057c478bd9Sstevel@tonic-gate } 41067c478bd9Sstevel@tonic-gate else 41077c478bd9Sstevel@tonic-gate { 41087c478bd9Sstevel@tonic-gate off = 4; 41097c478bd9Sstevel@tonic-gate } 41107c478bd9Sstevel@tonic-gate message("%s", statmsg + off); 41117c478bd9Sstevel@tonic-gate if (status == EX_TEMPFAIL && e->e_xfp != NULL) 41127c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 41137c478bd9Sstevel@tonic-gate &MsgBuf[4]); 41147c478bd9Sstevel@tonic-gate } 41157c478bd9Sstevel@tonic-gate else 41167c478bd9Sstevel@tonic-gate { 41177c478bd9Sstevel@tonic-gate char mbuf[ENHSCLEN + 4]; 41187c478bd9Sstevel@tonic-gate 41197c478bd9Sstevel@tonic-gate Errors++; 41207c478bd9Sstevel@tonic-gate if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 4121*058561cbSjbeck off < sizeof(mbuf) - 4) 41227c478bd9Sstevel@tonic-gate { 41237c478bd9Sstevel@tonic-gate if (dsn == NULL) 41247c478bd9Sstevel@tonic-gate { 4125*058561cbSjbeck (void) sm_snprintf(dsnbuf, sizeof(dsnbuf), 41267c478bd9Sstevel@tonic-gate "%.*s", off, statmsg + 4); 41277c478bd9Sstevel@tonic-gate dsn = dsnbuf; 41287c478bd9Sstevel@tonic-gate } 41297c478bd9Sstevel@tonic-gate off += 5; 41307c478bd9Sstevel@tonic-gate 41317c478bd9Sstevel@tonic-gate /* copy only part of statmsg to mbuf */ 41327c478bd9Sstevel@tonic-gate (void) sm_strlcpy(mbuf, statmsg, off); 4133*058561cbSjbeck (void) sm_strlcat(mbuf, " %s", sizeof(mbuf)); 41347c478bd9Sstevel@tonic-gate } 41357c478bd9Sstevel@tonic-gate else 41367c478bd9Sstevel@tonic-gate { 41377c478bd9Sstevel@tonic-gate dsnbuf[0] = '\0'; 4138*058561cbSjbeck (void) sm_snprintf(mbuf, sizeof(mbuf), "%.3s %%s", 41397c478bd9Sstevel@tonic-gate statmsg); 41407c478bd9Sstevel@tonic-gate off = 4; 41417c478bd9Sstevel@tonic-gate } 41427c478bd9Sstevel@tonic-gate usrerr(mbuf, &statmsg[off]); 41437c478bd9Sstevel@tonic-gate } 41447c478bd9Sstevel@tonic-gate 41457c478bd9Sstevel@tonic-gate /* 41467c478bd9Sstevel@tonic-gate ** Final cleanup. 41477c478bd9Sstevel@tonic-gate ** Log a record of the transaction. Compute the new 41487c478bd9Sstevel@tonic-gate ** ExitStat -- if we already had an error, stick with 41497c478bd9Sstevel@tonic-gate ** that. 41507c478bd9Sstevel@tonic-gate */ 41517c478bd9Sstevel@tonic-gate 41527c478bd9Sstevel@tonic-gate if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 41537c478bd9Sstevel@tonic-gate LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 41547c478bd9Sstevel@tonic-gate logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 41557c478bd9Sstevel@tonic-gate 41567c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 41577c478bd9Sstevel@tonic-gate sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 41587c478bd9Sstevel@tonic-gate status, 41597c478bd9Sstevel@tonic-gate dsn == NULL ? "<NULL>" : dsn, 41607c478bd9Sstevel@tonic-gate e->e_message == NULL ? "<NULL>" : e->e_message, 41617c478bd9Sstevel@tonic-gate errnum); 41627c478bd9Sstevel@tonic-gate 41637c478bd9Sstevel@tonic-gate if (status != EX_TEMPFAIL) 41647c478bd9Sstevel@tonic-gate setstat(status); 41657c478bd9Sstevel@tonic-gate if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 41667c478bd9Sstevel@tonic-gate e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 41677c478bd9Sstevel@tonic-gate if (status != EX_OK && to != NULL && to->q_message == NULL) 41687c478bd9Sstevel@tonic-gate { 41697c478bd9Sstevel@tonic-gate if (!usestat && e->e_message != NULL) 41707c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 41717c478bd9Sstevel@tonic-gate e->e_message); 41727c478bd9Sstevel@tonic-gate else 41737c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 41747c478bd9Sstevel@tonic-gate statmsg + off); 41757c478bd9Sstevel@tonic-gate } 41767c478bd9Sstevel@tonic-gate errno = 0; 41777c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 41787c478bd9Sstevel@tonic-gate } 41797c478bd9Sstevel@tonic-gate /* 41807c478bd9Sstevel@tonic-gate ** LOGDELIVERY -- log the delivery in the system log 41817c478bd9Sstevel@tonic-gate ** 41827c478bd9Sstevel@tonic-gate ** Care is taken to avoid logging lines that are too long, because 41837c478bd9Sstevel@tonic-gate ** some versions of syslog have an unfortunate proclivity for core 41847c478bd9Sstevel@tonic-gate ** dumping. This is a hack, to be sure, that is at best empirical. 41857c478bd9Sstevel@tonic-gate ** 41867c478bd9Sstevel@tonic-gate ** Parameters: 41877c478bd9Sstevel@tonic-gate ** m -- the mailer info. Can be NULL for initial queue. 41887c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info -- can be NULL if the 41897c478bd9Sstevel@tonic-gate ** log is occurring when no connection is active. 41907c478bd9Sstevel@tonic-gate ** dsn -- the DSN attached to the status. 41917c478bd9Sstevel@tonic-gate ** status -- the message to print for the status. 41927c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address for the to list. 41937c478bd9Sstevel@tonic-gate ** xstart -- the transaction start time, used for 41947c478bd9Sstevel@tonic-gate ** computing transaction delay. 41957c478bd9Sstevel@tonic-gate ** e -- the current envelope. 41967c478bd9Sstevel@tonic-gate ** 41977c478bd9Sstevel@tonic-gate ** Returns: 41987c478bd9Sstevel@tonic-gate ** none 41997c478bd9Sstevel@tonic-gate ** 42007c478bd9Sstevel@tonic-gate ** Side Effects: 42017c478bd9Sstevel@tonic-gate ** none 42027c478bd9Sstevel@tonic-gate */ 42037c478bd9Sstevel@tonic-gate 42047c478bd9Sstevel@tonic-gate void 42057c478bd9Sstevel@tonic-gate logdelivery(m, mci, dsn, status, ctladdr, xstart, e) 42067c478bd9Sstevel@tonic-gate MAILER *m; 42077c478bd9Sstevel@tonic-gate register MCI *mci; 42087c478bd9Sstevel@tonic-gate char *dsn; 42097c478bd9Sstevel@tonic-gate const char *status; 42107c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 42117c478bd9Sstevel@tonic-gate time_t xstart; 42127c478bd9Sstevel@tonic-gate register ENVELOPE *e; 42137c478bd9Sstevel@tonic-gate { 42147c478bd9Sstevel@tonic-gate register char *bp; 42157c478bd9Sstevel@tonic-gate register char *p; 42167c478bd9Sstevel@tonic-gate int l; 42177c478bd9Sstevel@tonic-gate time_t now = curtime(); 42187c478bd9Sstevel@tonic-gate char buf[1024]; 42197c478bd9Sstevel@tonic-gate 42207c478bd9Sstevel@tonic-gate #if (SYSLOG_BUFSIZE) >= 256 42217c478bd9Sstevel@tonic-gate /* ctladdr: max 106 bytes */ 42227c478bd9Sstevel@tonic-gate bp = buf; 42237c478bd9Sstevel@tonic-gate if (ctladdr != NULL) 42247c478bd9Sstevel@tonic-gate { 42257c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 42267c478bd9Sstevel@tonic-gate shortenstring(ctladdr->q_paddr, 83)); 42277c478bd9Sstevel@tonic-gate bp += strlen(bp); 42287c478bd9Sstevel@tonic-gate if (bitset(QGOODUID, ctladdr->q_flags)) 42297c478bd9Sstevel@tonic-gate { 42307c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 42317c478bd9Sstevel@tonic-gate (int) ctladdr->q_uid, 42327c478bd9Sstevel@tonic-gate (int) ctladdr->q_gid); 42337c478bd9Sstevel@tonic-gate bp += strlen(bp); 42347c478bd9Sstevel@tonic-gate } 42357c478bd9Sstevel@tonic-gate } 42367c478bd9Sstevel@tonic-gate 42377c478bd9Sstevel@tonic-gate /* delay & xdelay: max 41 bytes */ 42387c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 42397c478bd9Sstevel@tonic-gate pintvl(now - e->e_ctime, true)); 42407c478bd9Sstevel@tonic-gate bp += strlen(bp); 42417c478bd9Sstevel@tonic-gate 42427c478bd9Sstevel@tonic-gate if (xstart != (time_t) 0) 42437c478bd9Sstevel@tonic-gate { 42447c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 42457c478bd9Sstevel@tonic-gate pintvl(now - xstart, true)); 42467c478bd9Sstevel@tonic-gate bp += strlen(bp); 42477c478bd9Sstevel@tonic-gate } 42487c478bd9Sstevel@tonic-gate 42497c478bd9Sstevel@tonic-gate /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 42507c478bd9Sstevel@tonic-gate if (m != NULL) 42517c478bd9Sstevel@tonic-gate { 42527c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 42537c478bd9Sstevel@tonic-gate m->m_name); 42547c478bd9Sstevel@tonic-gate bp += strlen(bp); 42557c478bd9Sstevel@tonic-gate } 42567c478bd9Sstevel@tonic-gate 42577c478bd9Sstevel@tonic-gate /* pri: changes with each delivery attempt */ 42587c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 42597c478bd9Sstevel@tonic-gate e->e_msgpriority); 42607c478bd9Sstevel@tonic-gate bp += strlen(bp); 42617c478bd9Sstevel@tonic-gate 42627c478bd9Sstevel@tonic-gate /* relay: max 66 bytes for IPv4 addresses */ 42637c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 42647c478bd9Sstevel@tonic-gate { 42657c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 42667c478bd9Sstevel@tonic-gate 42677c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 42687c478bd9Sstevel@tonic-gate shortenstring(mci->mci_host, 40)); 42697c478bd9Sstevel@tonic-gate bp += strlen(bp); 42707c478bd9Sstevel@tonic-gate 42717c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 42727c478bd9Sstevel@tonic-gate { 42737c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 42747c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 42757c478bd9Sstevel@tonic-gate } 42767c478bd9Sstevel@tonic-gate } 42777c478bd9Sstevel@tonic-gate else if (strcmp(status, "quarantined") == 0) 42787c478bd9Sstevel@tonic-gate { 42797c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 42807c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 42817c478bd9Sstevel@tonic-gate ", quarantine=%s", 42827c478bd9Sstevel@tonic-gate shortenstring(e->e_quarmsg, 40)); 42837c478bd9Sstevel@tonic-gate } 42847c478bd9Sstevel@tonic-gate else if (strcmp(status, "queued") != 0) 42857c478bd9Sstevel@tonic-gate { 42867c478bd9Sstevel@tonic-gate p = macvalue('h', e); 42877c478bd9Sstevel@tonic-gate if (p != NULL && p[0] != '\0') 42887c478bd9Sstevel@tonic-gate { 42897c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 42907c478bd9Sstevel@tonic-gate ", relay=%s", shortenstring(p, 40)); 42917c478bd9Sstevel@tonic-gate } 42927c478bd9Sstevel@tonic-gate } 42937c478bd9Sstevel@tonic-gate bp += strlen(bp); 42947c478bd9Sstevel@tonic-gate 42957c478bd9Sstevel@tonic-gate /* dsn */ 42967c478bd9Sstevel@tonic-gate if (dsn != NULL && *dsn != '\0') 42977c478bd9Sstevel@tonic-gate { 42987c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 42997c478bd9Sstevel@tonic-gate shortenstring(dsn, ENHSCLEN)); 43007c478bd9Sstevel@tonic-gate bp += strlen(bp); 43017c478bd9Sstevel@tonic-gate } 43027c478bd9Sstevel@tonic-gate 43037c478bd9Sstevel@tonic-gate #if _FFR_LOG_NTRIES 43047c478bd9Sstevel@tonic-gate /* ntries */ 43057c478bd9Sstevel@tonic-gate if (e->e_ntries >= 0) 43067c478bd9Sstevel@tonic-gate { 43077c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 43087c478bd9Sstevel@tonic-gate ", ntries=%d", e->e_ntries + 1); 43097c478bd9Sstevel@tonic-gate bp += strlen(bp); 43107c478bd9Sstevel@tonic-gate } 43117c478bd9Sstevel@tonic-gate #endif /* _FFR_LOG_NTRIES */ 43127c478bd9Sstevel@tonic-gate 43137c478bd9Sstevel@tonic-gate # define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 43147c478bd9Sstevel@tonic-gate # if (STATLEN) < 63 43157c478bd9Sstevel@tonic-gate # undef STATLEN 43167c478bd9Sstevel@tonic-gate # define STATLEN 63 43177c478bd9Sstevel@tonic-gate # endif /* (STATLEN) < 63 */ 43187c478bd9Sstevel@tonic-gate # if (STATLEN) > 203 43197c478bd9Sstevel@tonic-gate # undef STATLEN 43207c478bd9Sstevel@tonic-gate # define STATLEN 203 43217c478bd9Sstevel@tonic-gate # endif /* (STATLEN) > 203 */ 43227c478bd9Sstevel@tonic-gate 43237c478bd9Sstevel@tonic-gate /* stat: max 210 bytes */ 4324*058561cbSjbeck if ((bp - buf) > (sizeof(buf) - ((STATLEN) + 20))) 43257c478bd9Sstevel@tonic-gate { 43267c478bd9Sstevel@tonic-gate /* desperation move -- truncate data */ 4327*058561cbSjbeck bp = buf + sizeof(buf) - ((STATLEN) + 17); 43287c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 43297c478bd9Sstevel@tonic-gate bp += 3; 43307c478bd9Sstevel@tonic-gate } 43317c478bd9Sstevel@tonic-gate 43327c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 43337c478bd9Sstevel@tonic-gate bp += strlen(bp); 43347c478bd9Sstevel@tonic-gate 43357c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 43367c478bd9Sstevel@tonic-gate SPACELEFT(buf, bp)); 43377c478bd9Sstevel@tonic-gate 43387c478bd9Sstevel@tonic-gate /* id, to: max 13 + TOBUFSIZE bytes */ 43397c478bd9Sstevel@tonic-gate l = SYSLOG_BUFSIZE - 100 - strlen(buf); 43407c478bd9Sstevel@tonic-gate if (l < 0) 43417c478bd9Sstevel@tonic-gate l = 0; 43427c478bd9Sstevel@tonic-gate p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 43437c478bd9Sstevel@tonic-gate while (strlen(p) >= l) 43447c478bd9Sstevel@tonic-gate { 43457c478bd9Sstevel@tonic-gate register char *q; 43467c478bd9Sstevel@tonic-gate 43477c478bd9Sstevel@tonic-gate for (q = p + l; q > p; q--) 43487c478bd9Sstevel@tonic-gate { 43497c478bd9Sstevel@tonic-gate if (*q == ',') 43507c478bd9Sstevel@tonic-gate break; 43517c478bd9Sstevel@tonic-gate } 43527c478bd9Sstevel@tonic-gate if (p == q) 43537c478bd9Sstevel@tonic-gate break; 43547c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 43557c478bd9Sstevel@tonic-gate (int) (++q - p), p, buf); 43567c478bd9Sstevel@tonic-gate p = q; 43577c478bd9Sstevel@tonic-gate } 43587c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 43597c478bd9Sstevel@tonic-gate 43607c478bd9Sstevel@tonic-gate #else /* (SYSLOG_BUFSIZE) >= 256 */ 43617c478bd9Sstevel@tonic-gate 43627c478bd9Sstevel@tonic-gate l = SYSLOG_BUFSIZE - 85; 43637c478bd9Sstevel@tonic-gate if (l < 0) 43647c478bd9Sstevel@tonic-gate l = 0; 43657c478bd9Sstevel@tonic-gate p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 43667c478bd9Sstevel@tonic-gate while (strlen(p) >= l) 43677c478bd9Sstevel@tonic-gate { 43687c478bd9Sstevel@tonic-gate register char *q; 43697c478bd9Sstevel@tonic-gate 43707c478bd9Sstevel@tonic-gate for (q = p + l; q > p; q--) 43717c478bd9Sstevel@tonic-gate { 43727c478bd9Sstevel@tonic-gate if (*q == ',') 43737c478bd9Sstevel@tonic-gate break; 43747c478bd9Sstevel@tonic-gate } 43757c478bd9Sstevel@tonic-gate if (p == q) 43767c478bd9Sstevel@tonic-gate break; 43777c478bd9Sstevel@tonic-gate 43787c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 43797c478bd9Sstevel@tonic-gate (int) (++q - p), p); 43807c478bd9Sstevel@tonic-gate p = q; 43817c478bd9Sstevel@tonic-gate } 43827c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 43837c478bd9Sstevel@tonic-gate 43847c478bd9Sstevel@tonic-gate if (ctladdr != NULL) 43857c478bd9Sstevel@tonic-gate { 43867c478bd9Sstevel@tonic-gate bp = buf; 43877c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 43887c478bd9Sstevel@tonic-gate shortenstring(ctladdr->q_paddr, 83)); 43897c478bd9Sstevel@tonic-gate bp += strlen(bp); 43907c478bd9Sstevel@tonic-gate if (bitset(QGOODUID, ctladdr->q_flags)) 43917c478bd9Sstevel@tonic-gate { 43927c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 43937c478bd9Sstevel@tonic-gate ctladdr->q_uid, ctladdr->q_gid); 43947c478bd9Sstevel@tonic-gate bp += strlen(bp); 43957c478bd9Sstevel@tonic-gate } 43967c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%s", buf); 43977c478bd9Sstevel@tonic-gate } 43987c478bd9Sstevel@tonic-gate bp = buf; 43997c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 44007c478bd9Sstevel@tonic-gate pintvl(now - e->e_ctime, true)); 44017c478bd9Sstevel@tonic-gate bp += strlen(bp); 44027c478bd9Sstevel@tonic-gate if (xstart != (time_t) 0) 44037c478bd9Sstevel@tonic-gate { 44047c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 44057c478bd9Sstevel@tonic-gate pintvl(now - xstart, true)); 44067c478bd9Sstevel@tonic-gate bp += strlen(bp); 44077c478bd9Sstevel@tonic-gate } 44087c478bd9Sstevel@tonic-gate 44097c478bd9Sstevel@tonic-gate if (m != NULL) 44107c478bd9Sstevel@tonic-gate { 44117c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 44127c478bd9Sstevel@tonic-gate m->m_name); 44137c478bd9Sstevel@tonic-gate bp += strlen(bp); 44147c478bd9Sstevel@tonic-gate } 44157c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 44167c478bd9Sstevel@tonic-gate 44177c478bd9Sstevel@tonic-gate buf[0] = '\0'; 44187c478bd9Sstevel@tonic-gate bp = buf; 44197c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 44207c478bd9Sstevel@tonic-gate { 44217c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 44227c478bd9Sstevel@tonic-gate 44237c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 44247c478bd9Sstevel@tonic-gate mci->mci_host); 44257c478bd9Sstevel@tonic-gate bp += strlen(bp); 44267c478bd9Sstevel@tonic-gate 44277c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 44287c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 44297c478bd9Sstevel@tonic-gate " [%.100s]", 44307c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 44317c478bd9Sstevel@tonic-gate } 44327c478bd9Sstevel@tonic-gate else if (strcmp(status, "quarantined") == 0) 44337c478bd9Sstevel@tonic-gate { 44347c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 44357c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 44367c478bd9Sstevel@tonic-gate ", quarantine=%.100s", 44377c478bd9Sstevel@tonic-gate e->e_quarmsg); 44387c478bd9Sstevel@tonic-gate } 44397c478bd9Sstevel@tonic-gate else if (strcmp(status, "queued") != 0) 44407c478bd9Sstevel@tonic-gate { 44417c478bd9Sstevel@tonic-gate p = macvalue('h', e); 44427c478bd9Sstevel@tonic-gate if (p != NULL && p[0] != '\0') 4443*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), "relay=%.100s", p); 44447c478bd9Sstevel@tonic-gate } 44457c478bd9Sstevel@tonic-gate if (buf[0] != '\0') 44467c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 44477c478bd9Sstevel@tonic-gate 44487c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 44497c478bd9Sstevel@tonic-gate #endif /* (SYSLOG_BUFSIZE) >= 256 */ 44507c478bd9Sstevel@tonic-gate } 44517c478bd9Sstevel@tonic-gate /* 44527c478bd9Sstevel@tonic-gate ** PUTFROMLINE -- output a UNIX-style from line (or whatever) 44537c478bd9Sstevel@tonic-gate ** 44547c478bd9Sstevel@tonic-gate ** This can be made an arbitrary message separator by changing $l 44557c478bd9Sstevel@tonic-gate ** 44567c478bd9Sstevel@tonic-gate ** One of the ugliest hacks seen by human eyes is contained herein: 44577c478bd9Sstevel@tonic-gate ** UUCP wants those stupid "remote from <host>" lines. Why oh why 44587c478bd9Sstevel@tonic-gate ** does a well-meaning programmer such as myself have to deal with 44597c478bd9Sstevel@tonic-gate ** this kind of antique garbage???? 44607c478bd9Sstevel@tonic-gate ** 44617c478bd9Sstevel@tonic-gate ** Parameters: 44627c478bd9Sstevel@tonic-gate ** mci -- the connection information. 44637c478bd9Sstevel@tonic-gate ** e -- the envelope. 44647c478bd9Sstevel@tonic-gate ** 44657c478bd9Sstevel@tonic-gate ** Returns: 4466445f2479Sjbeck ** true iff line was written successfully 44677c478bd9Sstevel@tonic-gate ** 44687c478bd9Sstevel@tonic-gate ** Side Effects: 44697c478bd9Sstevel@tonic-gate ** outputs some text to fp. 44707c478bd9Sstevel@tonic-gate */ 44717c478bd9Sstevel@tonic-gate 4472445f2479Sjbeck bool 44737c478bd9Sstevel@tonic-gate putfromline(mci, e) 44747c478bd9Sstevel@tonic-gate register MCI *mci; 44757c478bd9Sstevel@tonic-gate ENVELOPE *e; 44767c478bd9Sstevel@tonic-gate { 44777c478bd9Sstevel@tonic-gate char *template = UnixFromLine; 44787c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 44797c478bd9Sstevel@tonic-gate char xbuf[MAXLINE]; 44807c478bd9Sstevel@tonic-gate 44817c478bd9Sstevel@tonic-gate if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4482445f2479Sjbeck return true; 44837c478bd9Sstevel@tonic-gate 44847c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 44857c478bd9Sstevel@tonic-gate 44867c478bd9Sstevel@tonic-gate if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 44877c478bd9Sstevel@tonic-gate { 44887c478bd9Sstevel@tonic-gate char *bang; 44897c478bd9Sstevel@tonic-gate 4490*058561cbSjbeck expand("\201g", buf, sizeof(buf), e); 44917c478bd9Sstevel@tonic-gate bang = strchr(buf, '!'); 44927c478bd9Sstevel@tonic-gate if (bang == NULL) 44937c478bd9Sstevel@tonic-gate { 44947c478bd9Sstevel@tonic-gate char *at; 44957c478bd9Sstevel@tonic-gate char hname[MAXNAME]; 44967c478bd9Sstevel@tonic-gate 44977c478bd9Sstevel@tonic-gate /* 44987c478bd9Sstevel@tonic-gate ** If we can construct a UUCP path, do so 44997c478bd9Sstevel@tonic-gate */ 45007c478bd9Sstevel@tonic-gate 45017c478bd9Sstevel@tonic-gate at = strrchr(buf, '@'); 45027c478bd9Sstevel@tonic-gate if (at == NULL) 45037c478bd9Sstevel@tonic-gate { 4504*058561cbSjbeck expand("\201k", hname, sizeof(hname), e); 45057c478bd9Sstevel@tonic-gate at = hname; 45067c478bd9Sstevel@tonic-gate } 45077c478bd9Sstevel@tonic-gate else 45087c478bd9Sstevel@tonic-gate *at++ = '\0'; 4509*058561cbSjbeck (void) sm_snprintf(xbuf, sizeof(xbuf), 45107c478bd9Sstevel@tonic-gate "From %.800s \201d remote from %.100s\n", 45117c478bd9Sstevel@tonic-gate buf, at); 45127c478bd9Sstevel@tonic-gate } 45137c478bd9Sstevel@tonic-gate else 45147c478bd9Sstevel@tonic-gate { 45157c478bd9Sstevel@tonic-gate *bang++ = '\0'; 4516*058561cbSjbeck (void) sm_snprintf(xbuf, sizeof(xbuf), 45177c478bd9Sstevel@tonic-gate "From %.800s \201d remote from %.100s\n", 45187c478bd9Sstevel@tonic-gate bang, buf); 45197c478bd9Sstevel@tonic-gate template = xbuf; 45207c478bd9Sstevel@tonic-gate } 45217c478bd9Sstevel@tonic-gate } 4522*058561cbSjbeck expand(template, buf, sizeof(buf), e); 4523445f2479Sjbeck return putxline(buf, strlen(buf), mci, PXLF_HEADER); 45247c478bd9Sstevel@tonic-gate } 4525445f2479Sjbeck 45267c478bd9Sstevel@tonic-gate /* 45277c478bd9Sstevel@tonic-gate ** PUTBODY -- put the body of a message. 45287c478bd9Sstevel@tonic-gate ** 45297c478bd9Sstevel@tonic-gate ** Parameters: 45307c478bd9Sstevel@tonic-gate ** mci -- the connection information. 45317c478bd9Sstevel@tonic-gate ** e -- the envelope to put out. 45327c478bd9Sstevel@tonic-gate ** separator -- if non-NULL, a message separator that must 45337c478bd9Sstevel@tonic-gate ** not be permitted in the resulting message. 45347c478bd9Sstevel@tonic-gate ** 45357c478bd9Sstevel@tonic-gate ** Returns: 4536445f2479Sjbeck ** true iff message was written successfully 45377c478bd9Sstevel@tonic-gate ** 45387c478bd9Sstevel@tonic-gate ** Side Effects: 45397c478bd9Sstevel@tonic-gate ** The message is written onto fp. 45407c478bd9Sstevel@tonic-gate */ 45417c478bd9Sstevel@tonic-gate 45427c478bd9Sstevel@tonic-gate /* values for output state variable */ 454349218d4fSjbeck #define OSTATE_HEAD 0 /* at beginning of line */ 454449218d4fSjbeck #define OSTATE_CR 1 /* read a carriage return */ 454549218d4fSjbeck #define OSTATE_INLINE 2 /* putting rest of line */ 45467c478bd9Sstevel@tonic-gate 4547445f2479Sjbeck bool 45487c478bd9Sstevel@tonic-gate putbody(mci, e, separator) 45497c478bd9Sstevel@tonic-gate register MCI *mci; 45507c478bd9Sstevel@tonic-gate register ENVELOPE *e; 45517c478bd9Sstevel@tonic-gate char *separator; 45527c478bd9Sstevel@tonic-gate { 45537c478bd9Sstevel@tonic-gate bool dead = false; 4554445f2479Sjbeck bool ioerr = false; 4555445f2479Sjbeck int save_errno; 45567c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 45577c478bd9Sstevel@tonic-gate #if MIME8TO7 45587c478bd9Sstevel@tonic-gate char *boundaries[MAXMIMENESTING + 1]; 45597c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */ 45607c478bd9Sstevel@tonic-gate 45617c478bd9Sstevel@tonic-gate /* 45627c478bd9Sstevel@tonic-gate ** Output the body of the message 45637c478bd9Sstevel@tonic-gate */ 45647c478bd9Sstevel@tonic-gate 45657c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 45667c478bd9Sstevel@tonic-gate { 45677c478bd9Sstevel@tonic-gate char *df = queuename(e, DATAFL_LETTER); 45687c478bd9Sstevel@tonic-gate 45697c478bd9Sstevel@tonic-gate e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 45707c478bd9Sstevel@tonic-gate SM_IO_RDONLY_B, NULL); 45717c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 45727c478bd9Sstevel@tonic-gate { 45737c478bd9Sstevel@tonic-gate char *msg = "!putbody: Cannot open %s for %s from %s"; 45747c478bd9Sstevel@tonic-gate 45757c478bd9Sstevel@tonic-gate if (errno == ENOENT) 45767c478bd9Sstevel@tonic-gate msg++; 45777c478bd9Sstevel@tonic-gate syserr(msg, df, e->e_to, e->e_from.q_paddr); 45787c478bd9Sstevel@tonic-gate } 45797c478bd9Sstevel@tonic-gate 45807c478bd9Sstevel@tonic-gate } 45817c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 45827c478bd9Sstevel@tonic-gate { 45837c478bd9Sstevel@tonic-gate if (bitset(MCIF_INHEADER, mci->mci_flags)) 45847c478bd9Sstevel@tonic-gate { 4585445f2479Sjbeck if (!putline("", mci)) 4586445f2479Sjbeck goto writeerr; 45877c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_INHEADER; 45887c478bd9Sstevel@tonic-gate } 4589445f2479Sjbeck if (!putline("<<< No Message Collected >>>", mci)) 4590445f2479Sjbeck goto writeerr; 45917c478bd9Sstevel@tonic-gate goto endofmessage; 45927c478bd9Sstevel@tonic-gate } 45937c478bd9Sstevel@tonic-gate 45947c478bd9Sstevel@tonic-gate if (e->e_dfino == (ino_t) 0) 45957c478bd9Sstevel@tonic-gate { 45967c478bd9Sstevel@tonic-gate struct stat stbuf; 45977c478bd9Sstevel@tonic-gate 45987c478bd9Sstevel@tonic-gate if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 45997c478bd9Sstevel@tonic-gate < 0) 46007c478bd9Sstevel@tonic-gate e->e_dfino = -1; 46017c478bd9Sstevel@tonic-gate else 46027c478bd9Sstevel@tonic-gate { 46037c478bd9Sstevel@tonic-gate e->e_dfdev = stbuf.st_dev; 46047c478bd9Sstevel@tonic-gate e->e_dfino = stbuf.st_ino; 46057c478bd9Sstevel@tonic-gate } 46067c478bd9Sstevel@tonic-gate } 46077c478bd9Sstevel@tonic-gate 46087c478bd9Sstevel@tonic-gate /* paranoia: the data file should always be in a rewound state */ 46097c478bd9Sstevel@tonic-gate (void) bfrewind(e->e_dfp); 46107c478bd9Sstevel@tonic-gate 4611445f2479Sjbeck /* simulate an I/O timeout when used as source */ 4612445f2479Sjbeck if (tTd(84, 101)) 4613445f2479Sjbeck sleep(319); 4614445f2479Sjbeck 46157c478bd9Sstevel@tonic-gate #if MIME8TO7 46167c478bd9Sstevel@tonic-gate if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 46177c478bd9Sstevel@tonic-gate { 46187c478bd9Sstevel@tonic-gate /* 46197c478bd9Sstevel@tonic-gate ** Do 8 to 7 bit MIME conversion. 46207c478bd9Sstevel@tonic-gate */ 46217c478bd9Sstevel@tonic-gate 46227c478bd9Sstevel@tonic-gate /* make sure it looks like a MIME message */ 4623445f2479Sjbeck if (hvalue("MIME-Version", e->e_header) == NULL && 4624445f2479Sjbeck !putline("MIME-Version: 1.0", mci)) 4625445f2479Sjbeck goto writeerr; 46267c478bd9Sstevel@tonic-gate 46277c478bd9Sstevel@tonic-gate if (hvalue("Content-Type", e->e_header) == NULL) 46287c478bd9Sstevel@tonic-gate { 4629*058561cbSjbeck (void) sm_snprintf(buf, sizeof(buf), 46307c478bd9Sstevel@tonic-gate "Content-Type: text/plain; charset=%s", 46317c478bd9Sstevel@tonic-gate defcharset(e)); 4632445f2479Sjbeck if (!putline(buf, mci)) 4633445f2479Sjbeck goto writeerr; 46347c478bd9Sstevel@tonic-gate } 46357c478bd9Sstevel@tonic-gate 46367c478bd9Sstevel@tonic-gate /* now do the hard work */ 46377c478bd9Sstevel@tonic-gate boundaries[0] = NULL; 46387c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 46393ee0e492Sjbeck if (mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER, 0) == 4640445f2479Sjbeck SM_IO_EOF) 4641445f2479Sjbeck goto writeerr; 46427c478bd9Sstevel@tonic-gate } 46437c478bd9Sstevel@tonic-gate # if MIME7TO8 46447c478bd9Sstevel@tonic-gate else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 46457c478bd9Sstevel@tonic-gate { 4646445f2479Sjbeck if (!mime7to8(mci, e->e_header, e)) 4647445f2479Sjbeck goto writeerr; 46487c478bd9Sstevel@tonic-gate } 46497c478bd9Sstevel@tonic-gate # endif /* MIME7TO8 */ 46507c478bd9Sstevel@tonic-gate else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 46517c478bd9Sstevel@tonic-gate { 46527c478bd9Sstevel@tonic-gate bool oldsuprerrs = SuprErrs; 46537c478bd9Sstevel@tonic-gate 46547c478bd9Sstevel@tonic-gate /* Use mime8to7 to check multipart for MIME header overflows */ 46557c478bd9Sstevel@tonic-gate boundaries[0] = NULL; 46567c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 46577c478bd9Sstevel@tonic-gate 46587c478bd9Sstevel@tonic-gate /* 46597c478bd9Sstevel@tonic-gate ** If EF_DONT_MIME is set, we have a broken MIME message 46607c478bd9Sstevel@tonic-gate ** and don't want to generate a new bounce message whose 46617c478bd9Sstevel@tonic-gate ** body propagates the broken MIME. We can't just not call 46627c478bd9Sstevel@tonic-gate ** mime8to7() as is done above since we need the security 46637c478bd9Sstevel@tonic-gate ** checks. The best we can do is suppress the errors. 46647c478bd9Sstevel@tonic-gate */ 46657c478bd9Sstevel@tonic-gate 46667c478bd9Sstevel@tonic-gate if (bitset(EF_DONT_MIME, e->e_flags)) 46677c478bd9Sstevel@tonic-gate SuprErrs = true; 46687c478bd9Sstevel@tonic-gate 4669445f2479Sjbeck if (mime8to7(mci, e->e_header, e, boundaries, 46703ee0e492Sjbeck M87F_OUTER|M87F_NO8TO7, 0) == SM_IO_EOF) 4671445f2479Sjbeck goto writeerr; 46727c478bd9Sstevel@tonic-gate 46737c478bd9Sstevel@tonic-gate /* restore SuprErrs */ 46747c478bd9Sstevel@tonic-gate SuprErrs = oldsuprerrs; 46757c478bd9Sstevel@tonic-gate } 46767c478bd9Sstevel@tonic-gate else 46777c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */ 46787c478bd9Sstevel@tonic-gate { 46797c478bd9Sstevel@tonic-gate int ostate; 46807c478bd9Sstevel@tonic-gate register char *bp; 46817c478bd9Sstevel@tonic-gate register char *pbp; 46827c478bd9Sstevel@tonic-gate register int c; 46837c478bd9Sstevel@tonic-gate register char *xp; 46847c478bd9Sstevel@tonic-gate int padc; 46857c478bd9Sstevel@tonic-gate char *buflim; 46867c478bd9Sstevel@tonic-gate int pos = 0; 46877c478bd9Sstevel@tonic-gate char peekbuf[12]; 46887c478bd9Sstevel@tonic-gate 46897c478bd9Sstevel@tonic-gate if (bitset(MCIF_INHEADER, mci->mci_flags)) 46907c478bd9Sstevel@tonic-gate { 4691445f2479Sjbeck if (!putline("", mci)) 4692445f2479Sjbeck goto writeerr; 46937c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_INHEADER; 46947c478bd9Sstevel@tonic-gate } 46957c478bd9Sstevel@tonic-gate 46967c478bd9Sstevel@tonic-gate /* determine end of buffer; allow for short mailer lines */ 4697*058561cbSjbeck buflim = &buf[sizeof(buf) - 1]; 46987c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_linelimit > 0 && 4699*058561cbSjbeck mci->mci_mailer->m_linelimit < sizeof(buf) - 1) 47007c478bd9Sstevel@tonic-gate buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 47017c478bd9Sstevel@tonic-gate 47027c478bd9Sstevel@tonic-gate /* copy temp file to output with mapping */ 470349218d4fSjbeck ostate = OSTATE_HEAD; 47047c478bd9Sstevel@tonic-gate bp = buf; 47057c478bd9Sstevel@tonic-gate pbp = peekbuf; 47067c478bd9Sstevel@tonic-gate while (!sm_io_error(mci->mci_out) && !dead) 47077c478bd9Sstevel@tonic-gate { 47087c478bd9Sstevel@tonic-gate if (pbp > peekbuf) 47097c478bd9Sstevel@tonic-gate c = *--pbp; 47107c478bd9Sstevel@tonic-gate else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 47117c478bd9Sstevel@tonic-gate == SM_IO_EOF) 47127c478bd9Sstevel@tonic-gate break; 47137c478bd9Sstevel@tonic-gate if (bitset(MCIF_7BIT, mci->mci_flags)) 47147c478bd9Sstevel@tonic-gate c &= 0x7f; 47157c478bd9Sstevel@tonic-gate switch (ostate) 47167c478bd9Sstevel@tonic-gate { 471749218d4fSjbeck case OSTATE_HEAD: 47187c478bd9Sstevel@tonic-gate if (c == '\0' && 47197c478bd9Sstevel@tonic-gate bitnset(M_NONULLS, 47207c478bd9Sstevel@tonic-gate mci->mci_mailer->m_flags)) 47217c478bd9Sstevel@tonic-gate break; 47227c478bd9Sstevel@tonic-gate if (c != '\r' && c != '\n' && bp < buflim) 47237c478bd9Sstevel@tonic-gate { 47247c478bd9Sstevel@tonic-gate *bp++ = c; 47257c478bd9Sstevel@tonic-gate break; 47267c478bd9Sstevel@tonic-gate } 47277c478bd9Sstevel@tonic-gate 47287c478bd9Sstevel@tonic-gate /* check beginning of line for special cases */ 47297c478bd9Sstevel@tonic-gate *bp = '\0'; 47307c478bd9Sstevel@tonic-gate pos = 0; 47317c478bd9Sstevel@tonic-gate padc = SM_IO_EOF; 47327c478bd9Sstevel@tonic-gate if (buf[0] == 'F' && 47337c478bd9Sstevel@tonic-gate bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 47347c478bd9Sstevel@tonic-gate && strncmp(buf, "From ", 5) == 0) 47357c478bd9Sstevel@tonic-gate { 47367c478bd9Sstevel@tonic-gate padc = '>'; 47377c478bd9Sstevel@tonic-gate } 47387c478bd9Sstevel@tonic-gate if (buf[0] == '-' && buf[1] == '-' && 47397c478bd9Sstevel@tonic-gate separator != NULL) 47407c478bd9Sstevel@tonic-gate { 47417c478bd9Sstevel@tonic-gate /* possible separator */ 47427c478bd9Sstevel@tonic-gate int sl = strlen(separator); 47437c478bd9Sstevel@tonic-gate 47447c478bd9Sstevel@tonic-gate if (strncmp(&buf[2], separator, sl) 47457c478bd9Sstevel@tonic-gate == 0) 47467c478bd9Sstevel@tonic-gate padc = ' '; 47477c478bd9Sstevel@tonic-gate } 47487c478bd9Sstevel@tonic-gate if (buf[0] == '.' && 47497c478bd9Sstevel@tonic-gate bitnset(M_XDOT, mci->mci_mailer->m_flags)) 47507c478bd9Sstevel@tonic-gate { 47517c478bd9Sstevel@tonic-gate padc = '.'; 47527c478bd9Sstevel@tonic-gate } 47537c478bd9Sstevel@tonic-gate 47547c478bd9Sstevel@tonic-gate /* now copy out saved line */ 47557c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 47567c478bd9Sstevel@tonic-gate { 47577c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 47587c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 47597c478bd9Sstevel@tonic-gate "%05d >>> ", 47607c478bd9Sstevel@tonic-gate (int) CurrentPid); 47617c478bd9Sstevel@tonic-gate if (padc != SM_IO_EOF) 47627c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 47637c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 47647c478bd9Sstevel@tonic-gate padc); 47657c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 47667c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 47677c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 47687c478bd9Sstevel@tonic-gate (unsigned char) *xp); 47697c478bd9Sstevel@tonic-gate if (c == '\n') 47707c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 47717c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 47727c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 47737c478bd9Sstevel@tonic-gate } 47747c478bd9Sstevel@tonic-gate if (padc != SM_IO_EOF) 47757c478bd9Sstevel@tonic-gate { 47767c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 47777c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, padc) 47787c478bd9Sstevel@tonic-gate == SM_IO_EOF) 47797c478bd9Sstevel@tonic-gate { 47807c478bd9Sstevel@tonic-gate dead = true; 47817c478bd9Sstevel@tonic-gate continue; 47827c478bd9Sstevel@tonic-gate } 47837c478bd9Sstevel@tonic-gate pos++; 47847c478bd9Sstevel@tonic-gate } 47857c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 47867c478bd9Sstevel@tonic-gate { 47877c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 47887c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 47897c478bd9Sstevel@tonic-gate (unsigned char) *xp) 47907c478bd9Sstevel@tonic-gate == SM_IO_EOF) 47917c478bd9Sstevel@tonic-gate { 47927c478bd9Sstevel@tonic-gate dead = true; 47937c478bd9Sstevel@tonic-gate break; 47947c478bd9Sstevel@tonic-gate } 47957c478bd9Sstevel@tonic-gate } 47967c478bd9Sstevel@tonic-gate if (dead) 47977c478bd9Sstevel@tonic-gate continue; 47987c478bd9Sstevel@tonic-gate if (c == '\n') 47997c478bd9Sstevel@tonic-gate { 48007c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 48017c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 48027c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 48037c478bd9Sstevel@tonic-gate == SM_IO_EOF) 48047c478bd9Sstevel@tonic-gate break; 48057c478bd9Sstevel@tonic-gate pos = 0; 48067c478bd9Sstevel@tonic-gate } 48077c478bd9Sstevel@tonic-gate else 48087c478bd9Sstevel@tonic-gate { 48097c478bd9Sstevel@tonic-gate pos += bp - buf; 48107c478bd9Sstevel@tonic-gate if (c != '\r') 48117c478bd9Sstevel@tonic-gate { 48127c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 48137c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 48147c478bd9Sstevel@tonic-gate *pbp++ = c; 48157c478bd9Sstevel@tonic-gate } 48167c478bd9Sstevel@tonic-gate } 48177c478bd9Sstevel@tonic-gate 48187c478bd9Sstevel@tonic-gate bp = buf; 48197c478bd9Sstevel@tonic-gate 48207c478bd9Sstevel@tonic-gate /* determine next state */ 48217c478bd9Sstevel@tonic-gate if (c == '\n') 482249218d4fSjbeck ostate = OSTATE_HEAD; 48237c478bd9Sstevel@tonic-gate else if (c == '\r') 482449218d4fSjbeck ostate = OSTATE_CR; 48257c478bd9Sstevel@tonic-gate else 482649218d4fSjbeck ostate = OSTATE_INLINE; 48277c478bd9Sstevel@tonic-gate continue; 48287c478bd9Sstevel@tonic-gate 482949218d4fSjbeck case OSTATE_CR: 48307c478bd9Sstevel@tonic-gate if (c == '\n') 48317c478bd9Sstevel@tonic-gate { 48327c478bd9Sstevel@tonic-gate /* got CRLF */ 48337c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 48347c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 48357c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 48367c478bd9Sstevel@tonic-gate == SM_IO_EOF) 48377c478bd9Sstevel@tonic-gate continue; 48387c478bd9Sstevel@tonic-gate 48397c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 48407c478bd9Sstevel@tonic-gate { 48417c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 48427c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 48437c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 48447c478bd9Sstevel@tonic-gate } 4845*058561cbSjbeck pos = 0; 484649218d4fSjbeck ostate = OSTATE_HEAD; 48477c478bd9Sstevel@tonic-gate continue; 48487c478bd9Sstevel@tonic-gate } 48497c478bd9Sstevel@tonic-gate 48507c478bd9Sstevel@tonic-gate /* had a naked carriage return */ 48517c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 48527c478bd9Sstevel@tonic-gate *pbp++ = c; 48537c478bd9Sstevel@tonic-gate c = '\r'; 485449218d4fSjbeck ostate = OSTATE_INLINE; 48557c478bd9Sstevel@tonic-gate goto putch; 48567c478bd9Sstevel@tonic-gate 485749218d4fSjbeck case OSTATE_INLINE: 48587c478bd9Sstevel@tonic-gate if (c == '\r') 48597c478bd9Sstevel@tonic-gate { 486049218d4fSjbeck ostate = OSTATE_CR; 48617c478bd9Sstevel@tonic-gate continue; 48627c478bd9Sstevel@tonic-gate } 48637c478bd9Sstevel@tonic-gate if (c == '\0' && 48647c478bd9Sstevel@tonic-gate bitnset(M_NONULLS, 48657c478bd9Sstevel@tonic-gate mci->mci_mailer->m_flags)) 48667c478bd9Sstevel@tonic-gate break; 48677c478bd9Sstevel@tonic-gate putch: 48687c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_linelimit > 0 && 48697c478bd9Sstevel@tonic-gate pos >= mci->mci_mailer->m_linelimit - 1 && 48707c478bd9Sstevel@tonic-gate c != '\n') 48717c478bd9Sstevel@tonic-gate { 48727c478bd9Sstevel@tonic-gate int d; 48737c478bd9Sstevel@tonic-gate 48747c478bd9Sstevel@tonic-gate /* check next character for EOL */ 48757c478bd9Sstevel@tonic-gate if (pbp > peekbuf) 48767c478bd9Sstevel@tonic-gate d = *(pbp - 1); 48777c478bd9Sstevel@tonic-gate else if ((d = sm_io_getc(e->e_dfp, 48787c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT)) 48797c478bd9Sstevel@tonic-gate != SM_IO_EOF) 48807c478bd9Sstevel@tonic-gate { 48817c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 48827c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 48837c478bd9Sstevel@tonic-gate *pbp++ = d; 48847c478bd9Sstevel@tonic-gate } 48857c478bd9Sstevel@tonic-gate 48867c478bd9Sstevel@tonic-gate if (d == '\n' || d == SM_IO_EOF) 48877c478bd9Sstevel@tonic-gate { 48887c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 48897c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 48907c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 48917c478bd9Sstevel@tonic-gate (unsigned char) c); 48927c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 48937c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 48947c478bd9Sstevel@tonic-gate (unsigned char) c) 48957c478bd9Sstevel@tonic-gate == SM_IO_EOF) 48967c478bd9Sstevel@tonic-gate { 48977c478bd9Sstevel@tonic-gate dead = true; 48987c478bd9Sstevel@tonic-gate continue; 48997c478bd9Sstevel@tonic-gate } 49007c478bd9Sstevel@tonic-gate pos++; 49017c478bd9Sstevel@tonic-gate continue; 49027c478bd9Sstevel@tonic-gate } 49037c478bd9Sstevel@tonic-gate 49047c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 49057c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, '!') 49067c478bd9Sstevel@tonic-gate == SM_IO_EOF || 49077c478bd9Sstevel@tonic-gate sm_io_fputs(mci->mci_out, 49087c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49097c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 49107c478bd9Sstevel@tonic-gate == SM_IO_EOF) 49117c478bd9Sstevel@tonic-gate { 49127c478bd9Sstevel@tonic-gate dead = true; 49137c478bd9Sstevel@tonic-gate continue; 49147c478bd9Sstevel@tonic-gate } 49157c478bd9Sstevel@tonic-gate 49167c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 49177c478bd9Sstevel@tonic-gate { 49187c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 49197c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49207c478bd9Sstevel@tonic-gate "!%s", 49217c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 49227c478bd9Sstevel@tonic-gate } 492349218d4fSjbeck ostate = OSTATE_HEAD; 49247c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 49257c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 49267c478bd9Sstevel@tonic-gate *pbp++ = c; 49277c478bd9Sstevel@tonic-gate continue; 49287c478bd9Sstevel@tonic-gate } 49297c478bd9Sstevel@tonic-gate if (c == '\n') 49307c478bd9Sstevel@tonic-gate { 49317c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 49327c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 49337c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49347c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 49357c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 49367c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49377c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 49387c478bd9Sstevel@tonic-gate == SM_IO_EOF) 49397c478bd9Sstevel@tonic-gate continue; 49407c478bd9Sstevel@tonic-gate pos = 0; 494149218d4fSjbeck ostate = OSTATE_HEAD; 49427c478bd9Sstevel@tonic-gate } 49437c478bd9Sstevel@tonic-gate else 49447c478bd9Sstevel@tonic-gate { 49457c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 49467c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 49477c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49487c478bd9Sstevel@tonic-gate (unsigned char) c); 49497c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 49507c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49517c478bd9Sstevel@tonic-gate (unsigned char) c) 49527c478bd9Sstevel@tonic-gate == SM_IO_EOF) 49537c478bd9Sstevel@tonic-gate { 49547c478bd9Sstevel@tonic-gate dead = true; 49557c478bd9Sstevel@tonic-gate continue; 49567c478bd9Sstevel@tonic-gate } 49577c478bd9Sstevel@tonic-gate pos++; 495849218d4fSjbeck ostate = OSTATE_INLINE; 49597c478bd9Sstevel@tonic-gate } 49607c478bd9Sstevel@tonic-gate break; 49617c478bd9Sstevel@tonic-gate } 49627c478bd9Sstevel@tonic-gate } 49637c478bd9Sstevel@tonic-gate 49647c478bd9Sstevel@tonic-gate /* make sure we are at the beginning of a line */ 49657c478bd9Sstevel@tonic-gate if (bp > buf) 49667c478bd9Sstevel@tonic-gate { 49677c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 49687c478bd9Sstevel@tonic-gate { 49697c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 49707c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 49717c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49727c478bd9Sstevel@tonic-gate (unsigned char) *xp); 49737c478bd9Sstevel@tonic-gate } 49747c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 49757c478bd9Sstevel@tonic-gate { 49767c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 49777c478bd9Sstevel@tonic-gate (unsigned char) *xp) 49787c478bd9Sstevel@tonic-gate == SM_IO_EOF) 49797c478bd9Sstevel@tonic-gate { 49807c478bd9Sstevel@tonic-gate dead = true; 49817c478bd9Sstevel@tonic-gate break; 49827c478bd9Sstevel@tonic-gate } 49837c478bd9Sstevel@tonic-gate } 49847c478bd9Sstevel@tonic-gate pos += bp - buf; 49857c478bd9Sstevel@tonic-gate } 49867c478bd9Sstevel@tonic-gate if (!dead && pos > 0) 49877c478bd9Sstevel@tonic-gate { 49887c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 49897c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 49907c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 49917c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4992445f2479Sjbeck if (sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 4993445f2479Sjbeck mci->mci_mailer->m_eol) == SM_IO_EOF) 4994445f2479Sjbeck goto writeerr; 49957c478bd9Sstevel@tonic-gate } 49967c478bd9Sstevel@tonic-gate } 49977c478bd9Sstevel@tonic-gate 49987c478bd9Sstevel@tonic-gate if (sm_io_error(e->e_dfp)) 49997c478bd9Sstevel@tonic-gate { 50007c478bd9Sstevel@tonic-gate syserr("putbody: %s/%cf%s: read error", 50017c478bd9Sstevel@tonic-gate qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 50027c478bd9Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 50037c478bd9Sstevel@tonic-gate ExitStat = EX_IOERR; 5004445f2479Sjbeck ioerr = true; 50057c478bd9Sstevel@tonic-gate } 50067c478bd9Sstevel@tonic-gate 50077c478bd9Sstevel@tonic-gate endofmessage: 50087c478bd9Sstevel@tonic-gate /* 50097c478bd9Sstevel@tonic-gate ** Since mailfile() uses e_dfp in a child process, 50107c478bd9Sstevel@tonic-gate ** the file offset in the stdio library for the 50117c478bd9Sstevel@tonic-gate ** parent process will not agree with the in-kernel 50127c478bd9Sstevel@tonic-gate ** file offset since the file descriptor is shared 50137c478bd9Sstevel@tonic-gate ** between the processes. Therefore, it is vital 50147c478bd9Sstevel@tonic-gate ** that the file always be rewound. This forces the 50157c478bd9Sstevel@tonic-gate ** kernel offset (lseek) and stdio library (ftell) 50167c478bd9Sstevel@tonic-gate ** offset to match. 50177c478bd9Sstevel@tonic-gate */ 50187c478bd9Sstevel@tonic-gate 5019445f2479Sjbeck save_errno = errno; 50207c478bd9Sstevel@tonic-gate if (e->e_dfp != NULL) 50217c478bd9Sstevel@tonic-gate (void) bfrewind(e->e_dfp); 50227c478bd9Sstevel@tonic-gate 50237c478bd9Sstevel@tonic-gate /* some mailers want extra blank line at end of message */ 50247c478bd9Sstevel@tonic-gate if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 50257c478bd9Sstevel@tonic-gate buf[0] != '\0' && buf[0] != '\n') 50267c478bd9Sstevel@tonic-gate { 5027445f2479Sjbeck if (!putline("", mci)) 5028445f2479Sjbeck goto writeerr; 5029445f2479Sjbeck } 5030445f2479Sjbeck 5031445f2479Sjbeck if (!dead && 5032445f2479Sjbeck (sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF || 5033445f2479Sjbeck (sm_io_error(mci->mci_out) && errno != EPIPE))) 5034445f2479Sjbeck { 5035445f2479Sjbeck save_errno = errno; 50367c478bd9Sstevel@tonic-gate syserr("putbody: write error"); 50377c478bd9Sstevel@tonic-gate ExitStat = EX_IOERR; 5038445f2479Sjbeck ioerr = true; 50397c478bd9Sstevel@tonic-gate } 50407c478bd9Sstevel@tonic-gate 5041445f2479Sjbeck errno = save_errno; 5042445f2479Sjbeck return !dead && !ioerr; 5043445f2479Sjbeck 5044445f2479Sjbeck writeerr: 5045445f2479Sjbeck return false; 50467c478bd9Sstevel@tonic-gate } 5047445f2479Sjbeck 50487c478bd9Sstevel@tonic-gate /* 50497c478bd9Sstevel@tonic-gate ** MAILFILE -- Send a message to a file. 50507c478bd9Sstevel@tonic-gate ** 50517c478bd9Sstevel@tonic-gate ** If the file has the set-user-ID/set-group-ID bits set, but NO 50527c478bd9Sstevel@tonic-gate ** execute bits, sendmail will try to become the owner of that file 50537c478bd9Sstevel@tonic-gate ** rather than the real user. Obviously, this only works if 50547c478bd9Sstevel@tonic-gate ** sendmail runs as root. 50557c478bd9Sstevel@tonic-gate ** 50567c478bd9Sstevel@tonic-gate ** This could be done as a subordinate mailer, except that it 50577c478bd9Sstevel@tonic-gate ** is used implicitly to save messages in ~/dead.letter. We 50587c478bd9Sstevel@tonic-gate ** view this as being sufficiently important as to include it 50597c478bd9Sstevel@tonic-gate ** here. For example, if the system is dying, we shouldn't have 50607c478bd9Sstevel@tonic-gate ** to create another process plus some pipes to save the message. 50617c478bd9Sstevel@tonic-gate ** 50627c478bd9Sstevel@tonic-gate ** Parameters: 50637c478bd9Sstevel@tonic-gate ** filename -- the name of the file to send to. 50647c478bd9Sstevel@tonic-gate ** mailer -- mailer definition for recipient -- if NULL, 50657c478bd9Sstevel@tonic-gate ** use FileMailer. 50667c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address header -- includes 50677c478bd9Sstevel@tonic-gate ** the userid/groupid to be when sending. 50687c478bd9Sstevel@tonic-gate ** sfflags -- flags for opening. 50697c478bd9Sstevel@tonic-gate ** e -- the current envelope. 50707c478bd9Sstevel@tonic-gate ** 50717c478bd9Sstevel@tonic-gate ** Returns: 50727c478bd9Sstevel@tonic-gate ** The exit code associated with the operation. 50737c478bd9Sstevel@tonic-gate ** 50747c478bd9Sstevel@tonic-gate ** Side Effects: 50757c478bd9Sstevel@tonic-gate ** none. 50767c478bd9Sstevel@tonic-gate */ 50777c478bd9Sstevel@tonic-gate 50787c478bd9Sstevel@tonic-gate # define RETURN(st) exit(st); 50797c478bd9Sstevel@tonic-gate 50807c478bd9Sstevel@tonic-gate static jmp_buf CtxMailfileTimeout; 50817c478bd9Sstevel@tonic-gate 50827c478bd9Sstevel@tonic-gate int 50837c478bd9Sstevel@tonic-gate mailfile(filename, mailer, ctladdr, sfflags, e) 50847c478bd9Sstevel@tonic-gate char *volatile filename; 50857c478bd9Sstevel@tonic-gate MAILER *volatile mailer; 50867c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 50877c478bd9Sstevel@tonic-gate volatile long sfflags; 50887c478bd9Sstevel@tonic-gate register ENVELOPE *e; 50897c478bd9Sstevel@tonic-gate { 50907c478bd9Sstevel@tonic-gate register SM_FILE_T *f; 50917c478bd9Sstevel@tonic-gate register pid_t pid = -1; 50927c478bd9Sstevel@tonic-gate volatile int mode; 50937c478bd9Sstevel@tonic-gate int len; 50947c478bd9Sstevel@tonic-gate off_t curoff; 50957c478bd9Sstevel@tonic-gate bool suidwarn = geteuid() == 0; 50967c478bd9Sstevel@tonic-gate char *p; 50977c478bd9Sstevel@tonic-gate char *volatile realfile; 50987c478bd9Sstevel@tonic-gate SM_EVENT *ev; 50997c478bd9Sstevel@tonic-gate char buf[MAXPATHLEN]; 51007c478bd9Sstevel@tonic-gate char targetfile[MAXPATHLEN]; 51017c478bd9Sstevel@tonic-gate 51027c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 51037c478bd9Sstevel@tonic-gate { 51047c478bd9Sstevel@tonic-gate sm_dprintf("mailfile %s\n ctladdr=", filename); 51057c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ctladdr, false); 51067c478bd9Sstevel@tonic-gate } 51077c478bd9Sstevel@tonic-gate 51087c478bd9Sstevel@tonic-gate if (mailer == NULL) 51097c478bd9Sstevel@tonic-gate mailer = FileMailer; 51107c478bd9Sstevel@tonic-gate 51117c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 51127c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 51137c478bd9Sstevel@tonic-gate 51147c478bd9Sstevel@tonic-gate /* 51157c478bd9Sstevel@tonic-gate ** Special case /dev/null. This allows us to restrict file 51167c478bd9Sstevel@tonic-gate ** delivery to regular files only. 51177c478bd9Sstevel@tonic-gate */ 51187c478bd9Sstevel@tonic-gate 51197c478bd9Sstevel@tonic-gate if (sm_path_isdevnull(filename)) 51207c478bd9Sstevel@tonic-gate return EX_OK; 51217c478bd9Sstevel@tonic-gate 51227c478bd9Sstevel@tonic-gate /* check for 8-bit available */ 51237c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 51247c478bd9Sstevel@tonic-gate bitnset(M_7BITS, mailer->m_flags) && 51257c478bd9Sstevel@tonic-gate (bitset(EF_DONT_MIME, e->e_flags) || 51267c478bd9Sstevel@tonic-gate !(bitset(MM_MIME8BIT, MimeMode) || 51277c478bd9Sstevel@tonic-gate (bitset(EF_IS_MIME, e->e_flags) && 51287c478bd9Sstevel@tonic-gate bitset(MM_CVTMIME, MimeMode))))) 51297c478bd9Sstevel@tonic-gate { 51307c478bd9Sstevel@tonic-gate e->e_status = "5.6.3"; 51317c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 51327c478bd9Sstevel@tonic-gate "554 Cannot send 8-bit data to 7-bit destination"); 51337c478bd9Sstevel@tonic-gate errno = 0; 51347c478bd9Sstevel@tonic-gate return EX_DATAERR; 51357c478bd9Sstevel@tonic-gate } 51367c478bd9Sstevel@tonic-gate 51377c478bd9Sstevel@tonic-gate /* Find the actual file */ 51387c478bd9Sstevel@tonic-gate if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 51397c478bd9Sstevel@tonic-gate { 51407c478bd9Sstevel@tonic-gate len = strlen(SafeFileEnv); 51417c478bd9Sstevel@tonic-gate 51427c478bd9Sstevel@tonic-gate if (strncmp(SafeFileEnv, filename, len) == 0) 51437c478bd9Sstevel@tonic-gate filename += len; 51447c478bd9Sstevel@tonic-gate 5145*058561cbSjbeck if (len + strlen(filename) + 1 >= sizeof(targetfile)) 51467c478bd9Sstevel@tonic-gate { 51477c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s/%s)", 51487c478bd9Sstevel@tonic-gate SafeFileEnv, filename); 51497c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 51507c478bd9Sstevel@tonic-gate } 5151*058561cbSjbeck (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof(targetfile)); 51527c478bd9Sstevel@tonic-gate realfile = targetfile + len; 51537c478bd9Sstevel@tonic-gate if (*filename == '/') 51547c478bd9Sstevel@tonic-gate filename++; 51557c478bd9Sstevel@tonic-gate if (*filename != '\0') 51567c478bd9Sstevel@tonic-gate { 51577c478bd9Sstevel@tonic-gate /* paranoia: trailing / should be removed in readcf */ 51587c478bd9Sstevel@tonic-gate if (targetfile[len - 1] != '/') 51597c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, 5160*058561cbSjbeck "/", sizeof(targetfile)); 51617c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename, 5162*058561cbSjbeck sizeof(targetfile)); 51637c478bd9Sstevel@tonic-gate } 51647c478bd9Sstevel@tonic-gate } 51657c478bd9Sstevel@tonic-gate else if (mailer->m_rootdir != NULL) 51667c478bd9Sstevel@tonic-gate { 5167*058561cbSjbeck expand(mailer->m_rootdir, targetfile, sizeof(targetfile), e); 51687c478bd9Sstevel@tonic-gate len = strlen(targetfile); 51697c478bd9Sstevel@tonic-gate 51707c478bd9Sstevel@tonic-gate if (strncmp(targetfile, filename, len) == 0) 51717c478bd9Sstevel@tonic-gate filename += len; 51727c478bd9Sstevel@tonic-gate 5173*058561cbSjbeck if (len + strlen(filename) + 1 >= sizeof(targetfile)) 51747c478bd9Sstevel@tonic-gate { 51757c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s/%s)", 51767c478bd9Sstevel@tonic-gate targetfile, filename); 51777c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 51787c478bd9Sstevel@tonic-gate } 51797c478bd9Sstevel@tonic-gate realfile = targetfile + len; 51807c478bd9Sstevel@tonic-gate if (targetfile[len - 1] != '/') 5181*058561cbSjbeck (void) sm_strlcat(targetfile, "/", sizeof(targetfile)); 51827c478bd9Sstevel@tonic-gate if (*filename == '/') 51837c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename + 1, 5184*058561cbSjbeck sizeof(targetfile)); 51857c478bd9Sstevel@tonic-gate else 51867c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename, 5187*058561cbSjbeck sizeof(targetfile)); 51887c478bd9Sstevel@tonic-gate } 51897c478bd9Sstevel@tonic-gate else 51907c478bd9Sstevel@tonic-gate { 5191*058561cbSjbeck if (sm_strlcpy(targetfile, filename, sizeof(targetfile)) >= 5192*058561cbSjbeck sizeof(targetfile)) 51937c478bd9Sstevel@tonic-gate { 51947c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s)", filename); 51957c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 51967c478bd9Sstevel@tonic-gate } 51977c478bd9Sstevel@tonic-gate realfile = targetfile; 51987c478bd9Sstevel@tonic-gate } 51997c478bd9Sstevel@tonic-gate 52007c478bd9Sstevel@tonic-gate /* 52017c478bd9Sstevel@tonic-gate ** Fork so we can change permissions here. 52027c478bd9Sstevel@tonic-gate ** Note that we MUST use fork, not vfork, because of 52037c478bd9Sstevel@tonic-gate ** the complications of calling subroutines, etc. 52047c478bd9Sstevel@tonic-gate */ 52057c478bd9Sstevel@tonic-gate 52067c478bd9Sstevel@tonic-gate 52077c478bd9Sstevel@tonic-gate /* 52087c478bd9Sstevel@tonic-gate ** Dispose of SIGCHLD signal catchers that may be laying 52097c478bd9Sstevel@tonic-gate ** around so that the waitfor() below will get it. 52107c478bd9Sstevel@tonic-gate */ 52117c478bd9Sstevel@tonic-gate 52127c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 52137c478bd9Sstevel@tonic-gate 52147c478bd9Sstevel@tonic-gate DOFORK(fork); 52157c478bd9Sstevel@tonic-gate 52167c478bd9Sstevel@tonic-gate if (pid < 0) 52177c478bd9Sstevel@tonic-gate return EX_OSERR; 52187c478bd9Sstevel@tonic-gate else if (pid == 0) 52197c478bd9Sstevel@tonic-gate { 52207c478bd9Sstevel@tonic-gate /* child -- actually write to file */ 52217c478bd9Sstevel@tonic-gate struct stat stb; 52227c478bd9Sstevel@tonic-gate MCI mcibuf; 52237c478bd9Sstevel@tonic-gate int err; 52247c478bd9Sstevel@tonic-gate volatile int oflags = O_WRONLY|O_APPEND; 52257c478bd9Sstevel@tonic-gate 52267c478bd9Sstevel@tonic-gate /* Reset global flags */ 52277c478bd9Sstevel@tonic-gate RestartRequest = NULL; 52287c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 52297c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 52307c478bd9Sstevel@tonic-gate PendingSignal = 0; 52317c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 52327c478bd9Sstevel@tonic-gate 52337c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 52343ee0e492Sjbeck { 52353ee0e492Sjbeck int fd; 52363ee0e492Sjbeck 52373ee0e492Sjbeck fd = sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL); 52383ee0e492Sjbeck /* SM_ASSERT(fd >= 0); */ 52393ee0e492Sjbeck if (fd >= 0) 52403ee0e492Sjbeck (void) close(fd); 52413ee0e492Sjbeck } 52427c478bd9Sstevel@tonic-gate 52437c478bd9Sstevel@tonic-gate (void) sm_signal(SIGINT, SIG_DFL); 52447c478bd9Sstevel@tonic-gate (void) sm_signal(SIGHUP, SIG_DFL); 52457c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 52467c478bd9Sstevel@tonic-gate (void) umask(OldUmask); 52477c478bd9Sstevel@tonic-gate e->e_to = filename; 52487c478bd9Sstevel@tonic-gate ExitStat = EX_OK; 52497c478bd9Sstevel@tonic-gate 52507c478bd9Sstevel@tonic-gate if (setjmp(CtxMailfileTimeout) != 0) 52517c478bd9Sstevel@tonic-gate { 52527c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 52537c478bd9Sstevel@tonic-gate } 52547c478bd9Sstevel@tonic-gate 52557c478bd9Sstevel@tonic-gate if (TimeOuts.to_fileopen > 0) 52567c478bd9Sstevel@tonic-gate ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 52577c478bd9Sstevel@tonic-gate 0); 52587c478bd9Sstevel@tonic-gate else 52597c478bd9Sstevel@tonic-gate ev = NULL; 52607c478bd9Sstevel@tonic-gate 52617c478bd9Sstevel@tonic-gate /* check file mode to see if set-user-ID */ 52627c478bd9Sstevel@tonic-gate if (stat(targetfile, &stb) < 0) 52637c478bd9Sstevel@tonic-gate mode = FileMode; 52647c478bd9Sstevel@tonic-gate else 52657c478bd9Sstevel@tonic-gate mode = stb.st_mode; 52667c478bd9Sstevel@tonic-gate 52677c478bd9Sstevel@tonic-gate /* limit the errors to those actually caused in the child */ 52687c478bd9Sstevel@tonic-gate errno = 0; 52697c478bd9Sstevel@tonic-gate ExitStat = EX_OK; 52707c478bd9Sstevel@tonic-gate 52717c478bd9Sstevel@tonic-gate /* Allow alias expansions to use the S_IS{U,G}ID bits */ 52727c478bd9Sstevel@tonic-gate if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 52737c478bd9Sstevel@tonic-gate bitset(SFF_RUNASREALUID, sfflags)) 52747c478bd9Sstevel@tonic-gate { 52757c478bd9Sstevel@tonic-gate /* ignore set-user-ID and set-group-ID bits */ 52767c478bd9Sstevel@tonic-gate mode &= ~(S_ISGID|S_ISUID); 52777c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 52787c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 52797c478bd9Sstevel@tonic-gate } 52807c478bd9Sstevel@tonic-gate 52817c478bd9Sstevel@tonic-gate /* we have to open the data file BEFORE setuid() */ 52827c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 52837c478bd9Sstevel@tonic-gate { 52847c478bd9Sstevel@tonic-gate char *df = queuename(e, DATAFL_LETTER); 52857c478bd9Sstevel@tonic-gate 52867c478bd9Sstevel@tonic-gate e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 52877c478bd9Sstevel@tonic-gate SM_IO_RDONLY_B, NULL); 52887c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 52897c478bd9Sstevel@tonic-gate { 52907c478bd9Sstevel@tonic-gate syserr("mailfile: Cannot open %s for %s from %s", 52917c478bd9Sstevel@tonic-gate df, e->e_to, e->e_from.q_paddr); 52927c478bd9Sstevel@tonic-gate } 52937c478bd9Sstevel@tonic-gate } 52947c478bd9Sstevel@tonic-gate 52957c478bd9Sstevel@tonic-gate /* select a new user to run as */ 52967c478bd9Sstevel@tonic-gate if (!bitset(SFF_RUNASREALUID, sfflags)) 52977c478bd9Sstevel@tonic-gate { 52987c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 52997c478bd9Sstevel@tonic-gate { 53007c478bd9Sstevel@tonic-gate RealUserName = NULL; 53017c478bd9Sstevel@tonic-gate if (mailer->m_uid == NO_UID) 53027c478bd9Sstevel@tonic-gate RealUid = RunAsUid; 53037c478bd9Sstevel@tonic-gate else 53047c478bd9Sstevel@tonic-gate RealUid = mailer->m_uid; 53057c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && RealUid != RunAsUid) 53067c478bd9Sstevel@tonic-gate { 53077c478bd9Sstevel@tonic-gate /* Only root can change the uid */ 53087c478bd9Sstevel@tonic-gate syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 53097c478bd9Sstevel@tonic-gate (int) RunAsUid, (int) RealUid); 53107c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 53117c478bd9Sstevel@tonic-gate } 53127c478bd9Sstevel@tonic-gate } 53137c478bd9Sstevel@tonic-gate else if (bitset(S_ISUID, mode)) 53147c478bd9Sstevel@tonic-gate { 53157c478bd9Sstevel@tonic-gate RealUserName = NULL; 53167c478bd9Sstevel@tonic-gate RealUid = stb.st_uid; 53177c478bd9Sstevel@tonic-gate } 53187c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 53197c478bd9Sstevel@tonic-gate { 53207c478bd9Sstevel@tonic-gate if (ctladdr->q_ruser != NULL) 53217c478bd9Sstevel@tonic-gate RealUserName = ctladdr->q_ruser; 53227c478bd9Sstevel@tonic-gate else 53237c478bd9Sstevel@tonic-gate RealUserName = ctladdr->q_user; 53247c478bd9Sstevel@tonic-gate RealUid = ctladdr->q_uid; 53257c478bd9Sstevel@tonic-gate } 53267c478bd9Sstevel@tonic-gate else if (mailer != NULL && mailer->m_uid != NO_UID) 53277c478bd9Sstevel@tonic-gate { 53287c478bd9Sstevel@tonic-gate RealUserName = DefUser; 53297c478bd9Sstevel@tonic-gate RealUid = mailer->m_uid; 53307c478bd9Sstevel@tonic-gate } 53317c478bd9Sstevel@tonic-gate else 53327c478bd9Sstevel@tonic-gate { 53337c478bd9Sstevel@tonic-gate RealUserName = DefUser; 53347c478bd9Sstevel@tonic-gate RealUid = DefUid; 53357c478bd9Sstevel@tonic-gate } 53367c478bd9Sstevel@tonic-gate 53377c478bd9Sstevel@tonic-gate /* select a new group to run as */ 53387c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 53397c478bd9Sstevel@tonic-gate { 53407c478bd9Sstevel@tonic-gate if (mailer->m_gid == NO_GID) 53417c478bd9Sstevel@tonic-gate RealGid = RunAsGid; 53427c478bd9Sstevel@tonic-gate else 53437c478bd9Sstevel@tonic-gate RealGid = mailer->m_gid; 53447c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && 53457c478bd9Sstevel@tonic-gate (RealGid != getgid() || 53467c478bd9Sstevel@tonic-gate RealGid != getegid())) 53477c478bd9Sstevel@tonic-gate { 53487c478bd9Sstevel@tonic-gate /* Only root can change the gid */ 53497c478bd9Sstevel@tonic-gate syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 53507c478bd9Sstevel@tonic-gate (int) RealGid, (int) RunAsUid, 53517c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 53527c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 53537c478bd9Sstevel@tonic-gate } 53547c478bd9Sstevel@tonic-gate } 53557c478bd9Sstevel@tonic-gate else if (bitset(S_ISGID, mode)) 53567c478bd9Sstevel@tonic-gate RealGid = stb.st_gid; 53577c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && 53587c478bd9Sstevel@tonic-gate ctladdr->q_uid == DefUid && 53597c478bd9Sstevel@tonic-gate ctladdr->q_gid == 0) 53607c478bd9Sstevel@tonic-gate { 53617c478bd9Sstevel@tonic-gate /* 53627c478bd9Sstevel@tonic-gate ** Special case: This means it is an 53637c478bd9Sstevel@tonic-gate ** alias and we should act as DefaultUser. 53647c478bd9Sstevel@tonic-gate ** See alias()'s comments. 53657c478bd9Sstevel@tonic-gate */ 53667c478bd9Sstevel@tonic-gate 53677c478bd9Sstevel@tonic-gate RealGid = DefGid; 53687c478bd9Sstevel@tonic-gate RealUserName = DefUser; 53697c478bd9Sstevel@tonic-gate } 53707c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 53717c478bd9Sstevel@tonic-gate RealGid = ctladdr->q_gid; 53727c478bd9Sstevel@tonic-gate else if (mailer != NULL && mailer->m_gid != NO_GID) 53737c478bd9Sstevel@tonic-gate RealGid = mailer->m_gid; 53747c478bd9Sstevel@tonic-gate else 53757c478bd9Sstevel@tonic-gate RealGid = DefGid; 53767c478bd9Sstevel@tonic-gate } 53777c478bd9Sstevel@tonic-gate 53787c478bd9Sstevel@tonic-gate /* last ditch */ 53797c478bd9Sstevel@tonic-gate if (!bitset(SFF_ROOTOK, sfflags)) 53807c478bd9Sstevel@tonic-gate { 53817c478bd9Sstevel@tonic-gate if (RealUid == 0) 53827c478bd9Sstevel@tonic-gate RealUid = DefUid; 53837c478bd9Sstevel@tonic-gate if (RealGid == 0) 53847c478bd9Sstevel@tonic-gate RealGid = DefGid; 53857c478bd9Sstevel@tonic-gate } 53867c478bd9Sstevel@tonic-gate 53877c478bd9Sstevel@tonic-gate /* set group id list (needs /etc/group access) */ 53887c478bd9Sstevel@tonic-gate if (RealUserName != NULL && !DontInitGroups) 53897c478bd9Sstevel@tonic-gate { 53907c478bd9Sstevel@tonic-gate if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 53917c478bd9Sstevel@tonic-gate { 53927c478bd9Sstevel@tonic-gate syserr("mailfile: initgroups(%s, %d) failed", 53937c478bd9Sstevel@tonic-gate RealUserName, RealGid); 53947c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 53957c478bd9Sstevel@tonic-gate } 53967c478bd9Sstevel@tonic-gate } 53977c478bd9Sstevel@tonic-gate else 53987c478bd9Sstevel@tonic-gate { 53997c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 54007c478bd9Sstevel@tonic-gate 54017c478bd9Sstevel@tonic-gate gidset[0] = RealGid; 54027c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 && suidwarn) 54037c478bd9Sstevel@tonic-gate { 54047c478bd9Sstevel@tonic-gate syserr("mailfile: setgroups() failed"); 54057c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 54067c478bd9Sstevel@tonic-gate } 54077c478bd9Sstevel@tonic-gate } 54087c478bd9Sstevel@tonic-gate 54097c478bd9Sstevel@tonic-gate /* 54107c478bd9Sstevel@tonic-gate ** If you have a safe environment, go into it. 54117c478bd9Sstevel@tonic-gate */ 54127c478bd9Sstevel@tonic-gate 54137c478bd9Sstevel@tonic-gate if (realfile != targetfile) 54147c478bd9Sstevel@tonic-gate { 54157c478bd9Sstevel@tonic-gate char save; 54167c478bd9Sstevel@tonic-gate 54177c478bd9Sstevel@tonic-gate save = *realfile; 54187c478bd9Sstevel@tonic-gate *realfile = '\0'; 54197c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 54207c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: chroot %s\n", targetfile); 54217c478bd9Sstevel@tonic-gate if (chroot(targetfile) < 0) 54227c478bd9Sstevel@tonic-gate { 54237c478bd9Sstevel@tonic-gate syserr("mailfile: Cannot chroot(%s)", 54247c478bd9Sstevel@tonic-gate targetfile); 54257c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 54267c478bd9Sstevel@tonic-gate } 54277c478bd9Sstevel@tonic-gate *realfile = save; 54287c478bd9Sstevel@tonic-gate } 54297c478bd9Sstevel@tonic-gate 54307c478bd9Sstevel@tonic-gate if (tTd(11, 40)) 54317c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: deliver to %s\n", realfile); 54327c478bd9Sstevel@tonic-gate 54337c478bd9Sstevel@tonic-gate if (chdir("/") < 0) 54347c478bd9Sstevel@tonic-gate { 54357c478bd9Sstevel@tonic-gate syserr("mailfile: cannot chdir(/)"); 54367c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 54377c478bd9Sstevel@tonic-gate } 54387c478bd9Sstevel@tonic-gate 54397c478bd9Sstevel@tonic-gate /* now reset the group and user ids */ 54407c478bd9Sstevel@tonic-gate endpwent(); 54417c478bd9Sstevel@tonic-gate sm_mbdb_terminate(); 54427c478bd9Sstevel@tonic-gate if (setgid(RealGid) < 0 && suidwarn) 54437c478bd9Sstevel@tonic-gate { 54447c478bd9Sstevel@tonic-gate syserr("mailfile: setgid(%ld) failed", (long) RealGid); 54457c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 54467c478bd9Sstevel@tonic-gate } 54477c478bd9Sstevel@tonic-gate vendor_set_uid(RealUid); 54487c478bd9Sstevel@tonic-gate if (setuid(RealUid) < 0 && suidwarn) 54497c478bd9Sstevel@tonic-gate { 54507c478bd9Sstevel@tonic-gate syserr("mailfile: setuid(%ld) failed", (long) RealUid); 54517c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 54527c478bd9Sstevel@tonic-gate } 54537c478bd9Sstevel@tonic-gate 54547c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 54557c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 54567c478bd9Sstevel@tonic-gate (int) getuid(), (int) geteuid(), 54577c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 54587c478bd9Sstevel@tonic-gate 54597c478bd9Sstevel@tonic-gate 54607c478bd9Sstevel@tonic-gate /* move into some "safe" directory */ 54617c478bd9Sstevel@tonic-gate if (mailer->m_execdir != NULL) 54627c478bd9Sstevel@tonic-gate { 54637c478bd9Sstevel@tonic-gate char *q; 54647c478bd9Sstevel@tonic-gate 54657c478bd9Sstevel@tonic-gate for (p = mailer->m_execdir; p != NULL; p = q) 54667c478bd9Sstevel@tonic-gate { 54677c478bd9Sstevel@tonic-gate q = strchr(p, ':'); 54687c478bd9Sstevel@tonic-gate if (q != NULL) 54697c478bd9Sstevel@tonic-gate *q = '\0'; 5470*058561cbSjbeck expand(p, buf, sizeof(buf), e); 54717c478bd9Sstevel@tonic-gate if (q != NULL) 54727c478bd9Sstevel@tonic-gate *q++ = ':'; 54737c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 54747c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: trydir %s\n", 54757c478bd9Sstevel@tonic-gate buf); 54767c478bd9Sstevel@tonic-gate if (buf[0] != '\0' && chdir(buf) >= 0) 54777c478bd9Sstevel@tonic-gate break; 54787c478bd9Sstevel@tonic-gate } 54797c478bd9Sstevel@tonic-gate } 54807c478bd9Sstevel@tonic-gate 54817c478bd9Sstevel@tonic-gate /* 54827c478bd9Sstevel@tonic-gate ** Recheck the file after we have assumed the ID of the 54837c478bd9Sstevel@tonic-gate ** delivery user to make sure we can deliver to it as 54847c478bd9Sstevel@tonic-gate ** that user. This is necessary if sendmail is running 54857c478bd9Sstevel@tonic-gate ** as root and the file is on an NFS mount which treats 54867c478bd9Sstevel@tonic-gate ** root as nobody. 54877c478bd9Sstevel@tonic-gate */ 54887c478bd9Sstevel@tonic-gate 54897c478bd9Sstevel@tonic-gate #if HASLSTAT 54907c478bd9Sstevel@tonic-gate if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 54917c478bd9Sstevel@tonic-gate err = stat(realfile, &stb); 54927c478bd9Sstevel@tonic-gate else 54937c478bd9Sstevel@tonic-gate err = lstat(realfile, &stb); 54947c478bd9Sstevel@tonic-gate #else /* HASLSTAT */ 54957c478bd9Sstevel@tonic-gate err = stat(realfile, &stb); 54967c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */ 54977c478bd9Sstevel@tonic-gate 54987c478bd9Sstevel@tonic-gate if (err < 0) 54997c478bd9Sstevel@tonic-gate { 55007c478bd9Sstevel@tonic-gate stb.st_mode = ST_MODE_NOFILE; 55017c478bd9Sstevel@tonic-gate mode = FileMode; 55027c478bd9Sstevel@tonic-gate oflags |= O_CREAT|O_EXCL; 55037c478bd9Sstevel@tonic-gate } 55047c478bd9Sstevel@tonic-gate else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 55057c478bd9Sstevel@tonic-gate (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 55067c478bd9Sstevel@tonic-gate DontBlameSendmail) && 55077c478bd9Sstevel@tonic-gate stb.st_nlink != 1) || 55087c478bd9Sstevel@tonic-gate (realfile != targetfile && !S_ISREG(mode))) 55097c478bd9Sstevel@tonic-gate exit(EX_CANTCREAT); 55107c478bd9Sstevel@tonic-gate else 55117c478bd9Sstevel@tonic-gate mode = stb.st_mode; 55127c478bd9Sstevel@tonic-gate 55137c478bd9Sstevel@tonic-gate if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 55147c478bd9Sstevel@tonic-gate sfflags |= SFF_NOSLINK; 55157c478bd9Sstevel@tonic-gate if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 55167c478bd9Sstevel@tonic-gate sfflags |= SFF_NOHLINK; 55177c478bd9Sstevel@tonic-gate sfflags &= ~SFF_OPENASROOT; 55187c478bd9Sstevel@tonic-gate f = safefopen(realfile, oflags, mode, sfflags); 55197c478bd9Sstevel@tonic-gate if (f == NULL) 55207c478bd9Sstevel@tonic-gate { 55217c478bd9Sstevel@tonic-gate if (transienterror(errno)) 55227c478bd9Sstevel@tonic-gate { 55237c478bd9Sstevel@tonic-gate usrerr("454 4.3.0 cannot open %s: %s", 55247c478bd9Sstevel@tonic-gate shortenstring(realfile, MAXSHORTSTR), 55257c478bd9Sstevel@tonic-gate sm_errstring(errno)); 55267c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 55277c478bd9Sstevel@tonic-gate } 55287c478bd9Sstevel@tonic-gate else 55297c478bd9Sstevel@tonic-gate { 55307c478bd9Sstevel@tonic-gate usrerr("554 5.3.0 cannot open %s: %s", 55317c478bd9Sstevel@tonic-gate shortenstring(realfile, MAXSHORTSTR), 55327c478bd9Sstevel@tonic-gate sm_errstring(errno)); 55337c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 55347c478bd9Sstevel@tonic-gate } 55357c478bd9Sstevel@tonic-gate } 55367c478bd9Sstevel@tonic-gate if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 55377c478bd9Sstevel@tonic-gate &stb)) 55387c478bd9Sstevel@tonic-gate { 55397c478bd9Sstevel@tonic-gate syserr("554 5.3.0 file changed after open"); 55407c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 55417c478bd9Sstevel@tonic-gate } 55427c478bd9Sstevel@tonic-gate if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 55437c478bd9Sstevel@tonic-gate { 55447c478bd9Sstevel@tonic-gate syserr("554 5.3.0 cannot fstat %s", 55457c478bd9Sstevel@tonic-gate sm_errstring(errno)); 55467c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 55477c478bd9Sstevel@tonic-gate } 55487c478bd9Sstevel@tonic-gate 55497c478bd9Sstevel@tonic-gate curoff = stb.st_size; 55507c478bd9Sstevel@tonic-gate 55517c478bd9Sstevel@tonic-gate if (ev != NULL) 55527c478bd9Sstevel@tonic-gate sm_clrevent(ev); 55537c478bd9Sstevel@tonic-gate 5554*058561cbSjbeck memset(&mcibuf, '\0', sizeof(mcibuf)); 55557c478bd9Sstevel@tonic-gate mcibuf.mci_mailer = mailer; 55567c478bd9Sstevel@tonic-gate mcibuf.mci_out = f; 55577c478bd9Sstevel@tonic-gate if (bitnset(M_7BITS, mailer->m_flags)) 55587c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_7BIT; 55597c478bd9Sstevel@tonic-gate 55607c478bd9Sstevel@tonic-gate /* clear out per-message flags from connection structure */ 55617c478bd9Sstevel@tonic-gate mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 55627c478bd9Sstevel@tonic-gate 55637c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 55647c478bd9Sstevel@tonic-gate !bitset(EF_DONT_MIME, e->e_flags) && 55657c478bd9Sstevel@tonic-gate bitnset(M_7BITS, mailer->m_flags)) 55667c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_CVT8TO7; 55677c478bd9Sstevel@tonic-gate 55687c478bd9Sstevel@tonic-gate #if MIME7TO8 55697c478bd9Sstevel@tonic-gate if (bitnset(M_MAKE8BIT, mailer->m_flags) && 55707c478bd9Sstevel@tonic-gate !bitset(MCIF_7BIT, mcibuf.mci_flags) && 55717c478bd9Sstevel@tonic-gate (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 55727c478bd9Sstevel@tonic-gate (sm_strcasecmp(p, "quoted-printable") == 0 || 55737c478bd9Sstevel@tonic-gate sm_strcasecmp(p, "base64") == 0) && 55747c478bd9Sstevel@tonic-gate (p = hvalue("Content-Type", e->e_header)) != NULL) 55757c478bd9Sstevel@tonic-gate { 55767c478bd9Sstevel@tonic-gate /* may want to convert 7 -> 8 */ 55777c478bd9Sstevel@tonic-gate /* XXX should really parse it here -- and use a class XXX */ 55787c478bd9Sstevel@tonic-gate if (sm_strncasecmp(p, "text/plain", 10) == 0 && 55797c478bd9Sstevel@tonic-gate (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 55807c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_CVT7TO8; 55817c478bd9Sstevel@tonic-gate } 55827c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */ 55837c478bd9Sstevel@tonic-gate 5584445f2479Sjbeck if (!putfromline(&mcibuf, e) || 5585445f2479Sjbeck !(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER) || 5586445f2479Sjbeck !(*e->e_putbody)(&mcibuf, e, NULL) || 5587445f2479Sjbeck !putline("\n", &mcibuf) || 5588445f2479Sjbeck (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 55897c478bd9Sstevel@tonic-gate (SuperSafe != SAFE_NO && 55907c478bd9Sstevel@tonic-gate fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5591445f2479Sjbeck sm_io_error(f))) 55927c478bd9Sstevel@tonic-gate { 55937c478bd9Sstevel@tonic-gate setstat(EX_IOERR); 55947c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE 55957c478bd9Sstevel@tonic-gate (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 55967c478bd9Sstevel@tonic-gate curoff); 55977c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */ 55987c478bd9Sstevel@tonic-gate } 55997c478bd9Sstevel@tonic-gate 56007c478bd9Sstevel@tonic-gate /* reset ISUID & ISGID bits for paranoid systems */ 56017c478bd9Sstevel@tonic-gate #if HASFCHMOD 56027c478bd9Sstevel@tonic-gate (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 56037c478bd9Sstevel@tonic-gate (MODE_T) mode); 56047c478bd9Sstevel@tonic-gate #else /* HASFCHMOD */ 56057c478bd9Sstevel@tonic-gate (void) chmod(filename, (MODE_T) mode); 56067c478bd9Sstevel@tonic-gate #endif /* HASFCHMOD */ 56077c478bd9Sstevel@tonic-gate if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 56087c478bd9Sstevel@tonic-gate setstat(EX_IOERR); 56097c478bd9Sstevel@tonic-gate (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 56107c478bd9Sstevel@tonic-gate (void) setuid(RealUid); 56117c478bd9Sstevel@tonic-gate exit(ExitStat); 56127c478bd9Sstevel@tonic-gate /* NOTREACHED */ 56137c478bd9Sstevel@tonic-gate } 56147c478bd9Sstevel@tonic-gate else 56157c478bd9Sstevel@tonic-gate { 56167c478bd9Sstevel@tonic-gate /* parent -- wait for exit status */ 56177c478bd9Sstevel@tonic-gate int st; 56187c478bd9Sstevel@tonic-gate 56197c478bd9Sstevel@tonic-gate st = waitfor(pid); 56207c478bd9Sstevel@tonic-gate if (st == -1) 56217c478bd9Sstevel@tonic-gate { 56227c478bd9Sstevel@tonic-gate syserr("mailfile: %s: wait", mailer->m_name); 56237c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 56247c478bd9Sstevel@tonic-gate } 56257c478bd9Sstevel@tonic-gate if (WIFEXITED(st)) 56267c478bd9Sstevel@tonic-gate { 56277c478bd9Sstevel@tonic-gate errno = 0; 56287c478bd9Sstevel@tonic-gate return (WEXITSTATUS(st)); 56297c478bd9Sstevel@tonic-gate } 56307c478bd9Sstevel@tonic-gate else 56317c478bd9Sstevel@tonic-gate { 56327c478bd9Sstevel@tonic-gate syserr("mailfile: %s: child died on signal %d", 56337c478bd9Sstevel@tonic-gate mailer->m_name, st); 56347c478bd9Sstevel@tonic-gate return EX_UNAVAILABLE; 56357c478bd9Sstevel@tonic-gate } 56367c478bd9Sstevel@tonic-gate /* NOTREACHED */ 56377c478bd9Sstevel@tonic-gate } 56387c478bd9Sstevel@tonic-gate return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 56397c478bd9Sstevel@tonic-gate } 56407c478bd9Sstevel@tonic-gate 56417c478bd9Sstevel@tonic-gate static void 56427c478bd9Sstevel@tonic-gate mailfiletimeout(ignore) 56437c478bd9Sstevel@tonic-gate int ignore; 56447c478bd9Sstevel@tonic-gate { 56457c478bd9Sstevel@tonic-gate /* 56467c478bd9Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 56477c478bd9Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 56487c478bd9Sstevel@tonic-gate ** DOING. 56497c478bd9Sstevel@tonic-gate */ 56507c478bd9Sstevel@tonic-gate 56517c478bd9Sstevel@tonic-gate errno = ETIMEDOUT; 56527c478bd9Sstevel@tonic-gate longjmp(CtxMailfileTimeout, 1); 56537c478bd9Sstevel@tonic-gate } 56547c478bd9Sstevel@tonic-gate /* 56557c478bd9Sstevel@tonic-gate ** HOSTSIGNATURE -- return the "signature" for a host. 56567c478bd9Sstevel@tonic-gate ** 56577c478bd9Sstevel@tonic-gate ** The signature describes how we are going to send this -- it 56587c478bd9Sstevel@tonic-gate ** can be just the hostname (for non-Internet hosts) or can be 56597c478bd9Sstevel@tonic-gate ** an ordered list of MX hosts. 56607c478bd9Sstevel@tonic-gate ** 56617c478bd9Sstevel@tonic-gate ** Parameters: 56627c478bd9Sstevel@tonic-gate ** m -- the mailer describing this host. 56637c478bd9Sstevel@tonic-gate ** host -- the host name. 56647c478bd9Sstevel@tonic-gate ** 56657c478bd9Sstevel@tonic-gate ** Returns: 56667c478bd9Sstevel@tonic-gate ** The signature for this host. 56677c478bd9Sstevel@tonic-gate ** 56687c478bd9Sstevel@tonic-gate ** Side Effects: 56697c478bd9Sstevel@tonic-gate ** Can tweak the symbol table. 56707c478bd9Sstevel@tonic-gate */ 56717c478bd9Sstevel@tonic-gate 56727c478bd9Sstevel@tonic-gate #define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 56737c478bd9Sstevel@tonic-gate 56747c478bd9Sstevel@tonic-gate char * 56757c478bd9Sstevel@tonic-gate hostsignature(m, host) 56767c478bd9Sstevel@tonic-gate register MAILER *m; 56777c478bd9Sstevel@tonic-gate char *host; 56787c478bd9Sstevel@tonic-gate { 56797c478bd9Sstevel@tonic-gate register char *p; 56807c478bd9Sstevel@tonic-gate register STAB *s; 56817c478bd9Sstevel@tonic-gate time_t now; 56827c478bd9Sstevel@tonic-gate #if NAMED_BIND 56837c478bd9Sstevel@tonic-gate char sep = ':'; 56847c478bd9Sstevel@tonic-gate char prevsep = ':'; 56857c478bd9Sstevel@tonic-gate int i; 56867c478bd9Sstevel@tonic-gate int len; 56877c478bd9Sstevel@tonic-gate int nmx; 56887c478bd9Sstevel@tonic-gate int hl; 56897c478bd9Sstevel@tonic-gate char *hp; 56907c478bd9Sstevel@tonic-gate char *endp; 56917c478bd9Sstevel@tonic-gate int oldoptions = _res.options; 56927c478bd9Sstevel@tonic-gate char *mxhosts[MAXMXHOSTS + 1]; 56937c478bd9Sstevel@tonic-gate unsigned short mxprefs[MAXMXHOSTS + 1]; 56947c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 56957c478bd9Sstevel@tonic-gate 56967c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 56977c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(%s)\n", host); 56987c478bd9Sstevel@tonic-gate 56997c478bd9Sstevel@tonic-gate /* 57007c478bd9Sstevel@tonic-gate ** If local delivery (and not remote), just return a constant. 57017c478bd9Sstevel@tonic-gate */ 57027c478bd9Sstevel@tonic-gate 57037c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 57047c478bd9Sstevel@tonic-gate strcmp(m->m_mailer, "[IPC]") != 0 && 57057c478bd9Sstevel@tonic-gate !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 57067c478bd9Sstevel@tonic-gate return "localhost"; 57077c478bd9Sstevel@tonic-gate 57087c478bd9Sstevel@tonic-gate /* an empty host does not have MX records */ 57097c478bd9Sstevel@tonic-gate if (*host == '\0') 57107c478bd9Sstevel@tonic-gate return "_empty_"; 57117c478bd9Sstevel@tonic-gate 57127c478bd9Sstevel@tonic-gate /* 57137c478bd9Sstevel@tonic-gate ** Check to see if this uses IPC -- if not, it can't have MX records. 57147c478bd9Sstevel@tonic-gate */ 57157c478bd9Sstevel@tonic-gate 57167c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[IPC]") != 0 || 57177c478bd9Sstevel@tonic-gate CurEnv->e_sendmode == SM_DEFER) 57187c478bd9Sstevel@tonic-gate { 57197c478bd9Sstevel@tonic-gate /* just an ordinary mailer or deferred mode */ 57207c478bd9Sstevel@tonic-gate return host; 57217c478bd9Sstevel@tonic-gate } 57227c478bd9Sstevel@tonic-gate #if NETUNIX 57237c478bd9Sstevel@tonic-gate else if (m->m_argv[0] != NULL && 57247c478bd9Sstevel@tonic-gate strcmp(m->m_argv[0], "FILE") == 0) 57257c478bd9Sstevel@tonic-gate { 57267c478bd9Sstevel@tonic-gate /* rendezvous in the file system, no MX records */ 57277c478bd9Sstevel@tonic-gate return host; 57287c478bd9Sstevel@tonic-gate } 57297c478bd9Sstevel@tonic-gate #endif /* NETUNIX */ 57307c478bd9Sstevel@tonic-gate 57317c478bd9Sstevel@tonic-gate /* 57327c478bd9Sstevel@tonic-gate ** Look it up in the symbol table. 57337c478bd9Sstevel@tonic-gate */ 57347c478bd9Sstevel@tonic-gate 57357c478bd9Sstevel@tonic-gate now = curtime(); 57367c478bd9Sstevel@tonic-gate s = stab(host, ST_HOSTSIG, ST_ENTER); 57377c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 57387c478bd9Sstevel@tonic-gate { 57397c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_exp >= now) 57407c478bd9Sstevel@tonic-gate { 57417c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 57427c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 57437c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig); 57447c478bd9Sstevel@tonic-gate return s->s_hostsig.hs_sig; 57457c478bd9Sstevel@tonic-gate } 57467c478bd9Sstevel@tonic-gate 57477c478bd9Sstevel@tonic-gate /* signature is expired: clear it */ 57487c478bd9Sstevel@tonic-gate sm_free(s->s_hostsig.hs_sig); 57497c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = NULL; 57507c478bd9Sstevel@tonic-gate } 57517c478bd9Sstevel@tonic-gate 57527c478bd9Sstevel@tonic-gate /* set default TTL */ 57537c478bd9Sstevel@tonic-gate s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 57547c478bd9Sstevel@tonic-gate 57557c478bd9Sstevel@tonic-gate /* 57567c478bd9Sstevel@tonic-gate ** Not already there or expired -- create a signature. 57577c478bd9Sstevel@tonic-gate */ 57587c478bd9Sstevel@tonic-gate 57597c478bd9Sstevel@tonic-gate #if NAMED_BIND 57607c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 57617c478bd9Sstevel@tonic-gate _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 57627c478bd9Sstevel@tonic-gate 57637c478bd9Sstevel@tonic-gate for (hp = host; hp != NULL; hp = endp) 57647c478bd9Sstevel@tonic-gate { 57657c478bd9Sstevel@tonic-gate #if NETINET6 57667c478bd9Sstevel@tonic-gate if (*hp == '[') 57677c478bd9Sstevel@tonic-gate { 57687c478bd9Sstevel@tonic-gate endp = strchr(hp + 1, ']'); 57697c478bd9Sstevel@tonic-gate if (endp != NULL) 57707c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 57717c478bd9Sstevel@tonic-gate } 57727c478bd9Sstevel@tonic-gate else 57737c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 57747c478bd9Sstevel@tonic-gate #else /* NETINET6 */ 57757c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 57767c478bd9Sstevel@tonic-gate #endif /* NETINET6 */ 57777c478bd9Sstevel@tonic-gate if (endp != NULL) 57787c478bd9Sstevel@tonic-gate { 57797c478bd9Sstevel@tonic-gate sep = *endp; 57807c478bd9Sstevel@tonic-gate *endp = '\0'; 57817c478bd9Sstevel@tonic-gate } 57827c478bd9Sstevel@tonic-gate 57837c478bd9Sstevel@tonic-gate if (bitnset(M_NOMX, m->m_flags)) 57847c478bd9Sstevel@tonic-gate { 57857c478bd9Sstevel@tonic-gate /* skip MX lookups */ 57867c478bd9Sstevel@tonic-gate nmx = 1; 57877c478bd9Sstevel@tonic-gate mxhosts[0] = hp; 57887c478bd9Sstevel@tonic-gate } 57897c478bd9Sstevel@tonic-gate else 57907c478bd9Sstevel@tonic-gate { 57917c478bd9Sstevel@tonic-gate auto int rcode; 57927c478bd9Sstevel@tonic-gate int ttl; 57937c478bd9Sstevel@tonic-gate 57947c478bd9Sstevel@tonic-gate nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 57957c478bd9Sstevel@tonic-gate &ttl); 57967c478bd9Sstevel@tonic-gate if (nmx <= 0) 57977c478bd9Sstevel@tonic-gate { 57987c478bd9Sstevel@tonic-gate int save_errno; 57997c478bd9Sstevel@tonic-gate register MCI *mci; 58007c478bd9Sstevel@tonic-gate 58017c478bd9Sstevel@tonic-gate /* update the connection info for this host */ 58027c478bd9Sstevel@tonic-gate save_errno = errno; 58037c478bd9Sstevel@tonic-gate mci = mci_get(hp, m); 58047c478bd9Sstevel@tonic-gate mci->mci_errno = save_errno; 58057c478bd9Sstevel@tonic-gate mci->mci_herrno = h_errno; 58067c478bd9Sstevel@tonic-gate mci->mci_lastuse = now; 58077c478bd9Sstevel@tonic-gate if (rcode == EX_NOHOST) 58087c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, "5.1.2", 58097c478bd9Sstevel@tonic-gate "550 Host unknown"); 58107c478bd9Sstevel@tonic-gate else 58117c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, NULL, NULL); 58127c478bd9Sstevel@tonic-gate 58137c478bd9Sstevel@tonic-gate /* use the original host name as signature */ 58147c478bd9Sstevel@tonic-gate nmx = 1; 58157c478bd9Sstevel@tonic-gate mxhosts[0] = hp; 58167c478bd9Sstevel@tonic-gate } 58177c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 58187c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 58197c478bd9Sstevel@tonic-gate nmx, mxhosts[0]); 58207c478bd9Sstevel@tonic-gate 58217c478bd9Sstevel@tonic-gate /* 58227c478bd9Sstevel@tonic-gate ** Set new TTL: we use only one! 58237c478bd9Sstevel@tonic-gate ** We could try to use the minimum instead. 58247c478bd9Sstevel@tonic-gate */ 58257c478bd9Sstevel@tonic-gate 58267c478bd9Sstevel@tonic-gate s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 58277c478bd9Sstevel@tonic-gate } 58287c478bd9Sstevel@tonic-gate 58297c478bd9Sstevel@tonic-gate len = 0; 58307c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 58317c478bd9Sstevel@tonic-gate len += strlen(mxhosts[i]) + 1; 58327c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 58337c478bd9Sstevel@tonic-gate len += strlen(s->s_hostsig.hs_sig) + 1; 58347c478bd9Sstevel@tonic-gate if (len < 0 || len >= MAXHOSTSIGNATURE) 58357c478bd9Sstevel@tonic-gate { 58367c478bd9Sstevel@tonic-gate sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 58377c478bd9Sstevel@tonic-gate host, MAXHOSTSIGNATURE, len); 58387c478bd9Sstevel@tonic-gate len = MAXHOSTSIGNATURE; 58397c478bd9Sstevel@tonic-gate } 58407c478bd9Sstevel@tonic-gate p = sm_pmalloc_x(len); 58417c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 58427c478bd9Sstevel@tonic-gate { 58437c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 58447c478bd9Sstevel@tonic-gate sm_free(s->s_hostsig.hs_sig); /* XXX */ 58457c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = p; 58467c478bd9Sstevel@tonic-gate hl = strlen(p); 58477c478bd9Sstevel@tonic-gate p += hl; 58487c478bd9Sstevel@tonic-gate *p++ = prevsep; 58497c478bd9Sstevel@tonic-gate len -= hl + 1; 58507c478bd9Sstevel@tonic-gate } 58517c478bd9Sstevel@tonic-gate else 58527c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = p; 58537c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 58547c478bd9Sstevel@tonic-gate { 58557c478bd9Sstevel@tonic-gate hl = strlen(mxhosts[i]); 58567c478bd9Sstevel@tonic-gate if (len - 1 < hl || len <= 1) 58577c478bd9Sstevel@tonic-gate { 58587c478bd9Sstevel@tonic-gate /* force to drop out of outer loop */ 58597c478bd9Sstevel@tonic-gate len = -1; 58607c478bd9Sstevel@tonic-gate break; 58617c478bd9Sstevel@tonic-gate } 58627c478bd9Sstevel@tonic-gate if (i != 0) 58637c478bd9Sstevel@tonic-gate { 58647c478bd9Sstevel@tonic-gate if (mxprefs[i] == mxprefs[i - 1]) 58657c478bd9Sstevel@tonic-gate *p++ = ','; 58667c478bd9Sstevel@tonic-gate else 58677c478bd9Sstevel@tonic-gate *p++ = ':'; 58687c478bd9Sstevel@tonic-gate len--; 58697c478bd9Sstevel@tonic-gate } 58707c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, mxhosts[i], len); 58717c478bd9Sstevel@tonic-gate p += hl; 58727c478bd9Sstevel@tonic-gate len -= hl; 58737c478bd9Sstevel@tonic-gate } 58747c478bd9Sstevel@tonic-gate 58757c478bd9Sstevel@tonic-gate /* 58767c478bd9Sstevel@tonic-gate ** break out of loop if len exceeded MAXHOSTSIGNATURE 58777c478bd9Sstevel@tonic-gate ** because we won't have more space for further hosts 58787c478bd9Sstevel@tonic-gate ** anyway (separated by : in the .cf file). 58797c478bd9Sstevel@tonic-gate */ 58807c478bd9Sstevel@tonic-gate 58817c478bd9Sstevel@tonic-gate if (len < 0) 58827c478bd9Sstevel@tonic-gate break; 58837c478bd9Sstevel@tonic-gate if (endp != NULL) 58847c478bd9Sstevel@tonic-gate *endp++ = sep; 58857c478bd9Sstevel@tonic-gate prevsep = sep; 58867c478bd9Sstevel@tonic-gate } 58877c478bd9Sstevel@tonic-gate makelower(s->s_hostsig.hs_sig); 58887c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 58897c478bd9Sstevel@tonic-gate _res.options = oldoptions; 58907c478bd9Sstevel@tonic-gate #else /* NAMED_BIND */ 58917c478bd9Sstevel@tonic-gate /* not using BIND -- the signature is just the host name */ 58927c478bd9Sstevel@tonic-gate /* 58937c478bd9Sstevel@tonic-gate ** 'host' points to storage that will be freed after we are 58947c478bd9Sstevel@tonic-gate ** done processing the current envelope, so we copy it. 58957c478bd9Sstevel@tonic-gate */ 58967c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = sm_pstrdup_x(host); 58977c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 58987c478bd9Sstevel@tonic-gate if (tTd(17, 1)) 58997c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 59007c478bd9Sstevel@tonic-gate return s->s_hostsig.hs_sig; 59017c478bd9Sstevel@tonic-gate } 59027c478bd9Sstevel@tonic-gate /* 59037c478bd9Sstevel@tonic-gate ** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 59047c478bd9Sstevel@tonic-gate ** 59057c478bd9Sstevel@tonic-gate ** The signature describes how we are going to send this -- it 59067c478bd9Sstevel@tonic-gate ** can be just the hostname (for non-Internet hosts) or can be 59077c478bd9Sstevel@tonic-gate ** an ordered list of MX hosts which must be randomized for equal 59087c478bd9Sstevel@tonic-gate ** MX preference values. 59097c478bd9Sstevel@tonic-gate ** 59107c478bd9Sstevel@tonic-gate ** Parameters: 59117c478bd9Sstevel@tonic-gate ** sig -- the host signature. 59127c478bd9Sstevel@tonic-gate ** mxhosts -- array to populate. 59137c478bd9Sstevel@tonic-gate ** mailer -- mailer. 59147c478bd9Sstevel@tonic-gate ** 59157c478bd9Sstevel@tonic-gate ** Returns: 59167c478bd9Sstevel@tonic-gate ** The number of hosts inserted into mxhosts array. 59177c478bd9Sstevel@tonic-gate ** 59187c478bd9Sstevel@tonic-gate ** Side Effects: 59197c478bd9Sstevel@tonic-gate ** Randomizes equal MX preference hosts in mxhosts. 59207c478bd9Sstevel@tonic-gate */ 59217c478bd9Sstevel@tonic-gate 59227c478bd9Sstevel@tonic-gate static int 59237c478bd9Sstevel@tonic-gate parse_hostsignature(sig, mxhosts, mailer) 59247c478bd9Sstevel@tonic-gate char *sig; 59257c478bd9Sstevel@tonic-gate char **mxhosts; 59267c478bd9Sstevel@tonic-gate MAILER *mailer; 59277c478bd9Sstevel@tonic-gate { 59287c478bd9Sstevel@tonic-gate unsigned short curpref = 0; 59297c478bd9Sstevel@tonic-gate int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 59307c478bd9Sstevel@tonic-gate char *hp, *endp; 59317c478bd9Sstevel@tonic-gate unsigned short prefer[MAXMXHOSTS]; 59327c478bd9Sstevel@tonic-gate long rndm[MAXMXHOSTS]; 59337c478bd9Sstevel@tonic-gate 59347c478bd9Sstevel@tonic-gate for (hp = sig; hp != NULL; hp = endp) 59357c478bd9Sstevel@tonic-gate { 59367c478bd9Sstevel@tonic-gate char sep = ':'; 59377c478bd9Sstevel@tonic-gate 59387c478bd9Sstevel@tonic-gate #if NETINET6 59397c478bd9Sstevel@tonic-gate if (*hp == '[') 59407c478bd9Sstevel@tonic-gate { 59417c478bd9Sstevel@tonic-gate endp = strchr(hp + 1, ']'); 59427c478bd9Sstevel@tonic-gate if (endp != NULL) 59437c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 59447c478bd9Sstevel@tonic-gate } 59457c478bd9Sstevel@tonic-gate else 59467c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 59477c478bd9Sstevel@tonic-gate #else /* NETINET6 */ 59487c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 59497c478bd9Sstevel@tonic-gate #endif /* NETINET6 */ 59507c478bd9Sstevel@tonic-gate if (endp != NULL) 59517c478bd9Sstevel@tonic-gate { 59527c478bd9Sstevel@tonic-gate sep = *endp; 59537c478bd9Sstevel@tonic-gate *endp = '\0'; 59547c478bd9Sstevel@tonic-gate } 59557c478bd9Sstevel@tonic-gate 59567c478bd9Sstevel@tonic-gate mxhosts[nmx] = hp; 59577c478bd9Sstevel@tonic-gate prefer[nmx] = curpref; 59587c478bd9Sstevel@tonic-gate if (mci_match(hp, mailer)) 59597c478bd9Sstevel@tonic-gate rndm[nmx] = 0; 59607c478bd9Sstevel@tonic-gate else 59617c478bd9Sstevel@tonic-gate rndm[nmx] = get_random(); 59627c478bd9Sstevel@tonic-gate 59637c478bd9Sstevel@tonic-gate if (endp != NULL) 59647c478bd9Sstevel@tonic-gate { 59657c478bd9Sstevel@tonic-gate /* 59667c478bd9Sstevel@tonic-gate ** Since we don't have the original MX prefs, 59677c478bd9Sstevel@tonic-gate ** make our own. If the separator is a ':', that 59687c478bd9Sstevel@tonic-gate ** means the preference for the next host will be 59697c478bd9Sstevel@tonic-gate ** higher than this one, so simply increment curpref. 59707c478bd9Sstevel@tonic-gate */ 59717c478bd9Sstevel@tonic-gate 59727c478bd9Sstevel@tonic-gate if (sep == ':') 59737c478bd9Sstevel@tonic-gate curpref++; 59747c478bd9Sstevel@tonic-gate 59757c478bd9Sstevel@tonic-gate *endp++ = sep; 59767c478bd9Sstevel@tonic-gate } 59777c478bd9Sstevel@tonic-gate if (++nmx >= MAXMXHOSTS) 59787c478bd9Sstevel@tonic-gate break; 59797c478bd9Sstevel@tonic-gate } 59807c478bd9Sstevel@tonic-gate 59817c478bd9Sstevel@tonic-gate /* sort the records using the random factor for equal preferences */ 59827c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 59837c478bd9Sstevel@tonic-gate { 59847c478bd9Sstevel@tonic-gate for (j = i + 1; j < nmx; j++) 59857c478bd9Sstevel@tonic-gate { 59867c478bd9Sstevel@tonic-gate /* 59877c478bd9Sstevel@tonic-gate ** List is already sorted by MX preference, only 59887c478bd9Sstevel@tonic-gate ** need to look for equal preference MX records 59897c478bd9Sstevel@tonic-gate */ 59907c478bd9Sstevel@tonic-gate 59917c478bd9Sstevel@tonic-gate if (prefer[i] < prefer[j]) 59927c478bd9Sstevel@tonic-gate break; 59937c478bd9Sstevel@tonic-gate 59947c478bd9Sstevel@tonic-gate if (prefer[i] > prefer[j] || 59957c478bd9Sstevel@tonic-gate (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 59967c478bd9Sstevel@tonic-gate { 59977c478bd9Sstevel@tonic-gate register unsigned short tempp; 59987c478bd9Sstevel@tonic-gate register long tempr; 59997c478bd9Sstevel@tonic-gate register char *temp1; 60007c478bd9Sstevel@tonic-gate 60017c478bd9Sstevel@tonic-gate tempp = prefer[i]; 60027c478bd9Sstevel@tonic-gate prefer[i] = prefer[j]; 60037c478bd9Sstevel@tonic-gate prefer[j] = tempp; 60047c478bd9Sstevel@tonic-gate temp1 = mxhosts[i]; 60057c478bd9Sstevel@tonic-gate mxhosts[i] = mxhosts[j]; 60067c478bd9Sstevel@tonic-gate mxhosts[j] = temp1; 60077c478bd9Sstevel@tonic-gate tempr = rndm[i]; 60087c478bd9Sstevel@tonic-gate rndm[i] = rndm[j]; 60097c478bd9Sstevel@tonic-gate rndm[j] = tempr; 60107c478bd9Sstevel@tonic-gate } 60117c478bd9Sstevel@tonic-gate } 60127c478bd9Sstevel@tonic-gate } 60137c478bd9Sstevel@tonic-gate return nmx; 60147c478bd9Sstevel@tonic-gate } 60157c478bd9Sstevel@tonic-gate 60167c478bd9Sstevel@tonic-gate # if STARTTLS 60177c478bd9Sstevel@tonic-gate static SSL_CTX *clt_ctx = NULL; 60187c478bd9Sstevel@tonic-gate static bool tls_ok_clt = true; 60197c478bd9Sstevel@tonic-gate 60207c478bd9Sstevel@tonic-gate /* 60217c478bd9Sstevel@tonic-gate ** SETCLTTLS -- client side TLS: allow/disallow. 60227c478bd9Sstevel@tonic-gate ** 60237c478bd9Sstevel@tonic-gate ** Parameters: 60247c478bd9Sstevel@tonic-gate ** tls_ok -- should tls be done? 60257c478bd9Sstevel@tonic-gate ** 60267c478bd9Sstevel@tonic-gate ** Returns: 60277c478bd9Sstevel@tonic-gate ** none. 60287c478bd9Sstevel@tonic-gate ** 60297c478bd9Sstevel@tonic-gate ** Side Effects: 60307c478bd9Sstevel@tonic-gate ** sets tls_ok_clt (static variable in this module) 60317c478bd9Sstevel@tonic-gate */ 60327c478bd9Sstevel@tonic-gate 60337c478bd9Sstevel@tonic-gate void 60347c478bd9Sstevel@tonic-gate setclttls(tls_ok) 60357c478bd9Sstevel@tonic-gate bool tls_ok; 60367c478bd9Sstevel@tonic-gate { 60377c478bd9Sstevel@tonic-gate tls_ok_clt = tls_ok; 60387c478bd9Sstevel@tonic-gate return; 60397c478bd9Sstevel@tonic-gate } 60407c478bd9Sstevel@tonic-gate /* 60417c478bd9Sstevel@tonic-gate ** INITCLTTLS -- initialize client side TLS 60427c478bd9Sstevel@tonic-gate ** 60437c478bd9Sstevel@tonic-gate ** Parameters: 60447c478bd9Sstevel@tonic-gate ** tls_ok -- should tls initialization be done? 60457c478bd9Sstevel@tonic-gate ** 60467c478bd9Sstevel@tonic-gate ** Returns: 60477c478bd9Sstevel@tonic-gate ** succeeded? 60487c478bd9Sstevel@tonic-gate ** 60497c478bd9Sstevel@tonic-gate ** Side Effects: 60507c478bd9Sstevel@tonic-gate ** sets tls_ok_clt (static variable in this module) 60517c478bd9Sstevel@tonic-gate */ 60527c478bd9Sstevel@tonic-gate 60537c478bd9Sstevel@tonic-gate bool 60547c478bd9Sstevel@tonic-gate initclttls(tls_ok) 60557c478bd9Sstevel@tonic-gate bool tls_ok; 60567c478bd9Sstevel@tonic-gate { 60577c478bd9Sstevel@tonic-gate if (!tls_ok_clt) 60587c478bd9Sstevel@tonic-gate return false; 60597c478bd9Sstevel@tonic-gate tls_ok_clt = tls_ok; 60607c478bd9Sstevel@tonic-gate if (!tls_ok_clt) 60617c478bd9Sstevel@tonic-gate return false; 60627c478bd9Sstevel@tonic-gate if (clt_ctx != NULL) 60637c478bd9Sstevel@tonic-gate return true; /* already done */ 60647c478bd9Sstevel@tonic-gate tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 60657c478bd9Sstevel@tonic-gate CltKeyFile, CACertPath, CACertFile, DHParams); 60667c478bd9Sstevel@tonic-gate return tls_ok_clt; 60677c478bd9Sstevel@tonic-gate } 60687c478bd9Sstevel@tonic-gate 60697c478bd9Sstevel@tonic-gate /* 60707c478bd9Sstevel@tonic-gate ** STARTTLS -- try to start secure connection (client side) 60717c478bd9Sstevel@tonic-gate ** 60727c478bd9Sstevel@tonic-gate ** Parameters: 60737c478bd9Sstevel@tonic-gate ** m -- the mailer. 60747c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 60757c478bd9Sstevel@tonic-gate ** e -- the envelope. 60767c478bd9Sstevel@tonic-gate ** 60777c478bd9Sstevel@tonic-gate ** Returns: 60787c478bd9Sstevel@tonic-gate ** success? 60797c478bd9Sstevel@tonic-gate ** (maybe this should be some other code than EX_ 60807c478bd9Sstevel@tonic-gate ** that denotes which stage failed.) 60817c478bd9Sstevel@tonic-gate */ 60827c478bd9Sstevel@tonic-gate 60837c478bd9Sstevel@tonic-gate static int 60847c478bd9Sstevel@tonic-gate starttls(m, mci, e) 60857c478bd9Sstevel@tonic-gate MAILER *m; 60867c478bd9Sstevel@tonic-gate MCI *mci; 60877c478bd9Sstevel@tonic-gate ENVELOPE *e; 60887c478bd9Sstevel@tonic-gate { 60897c478bd9Sstevel@tonic-gate int smtpresult; 60907c478bd9Sstevel@tonic-gate int result = 0; 60917c478bd9Sstevel@tonic-gate int rfd, wfd; 60927c478bd9Sstevel@tonic-gate SSL *clt_ssl = NULL; 60937c478bd9Sstevel@tonic-gate time_t tlsstart; 60947c478bd9Sstevel@tonic-gate 60957c478bd9Sstevel@tonic-gate if (clt_ctx == NULL && !initclttls(true)) 60967c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 60977c478bd9Sstevel@tonic-gate smtpmessage("STARTTLS", m, mci); 60987c478bd9Sstevel@tonic-gate 60997c478bd9Sstevel@tonic-gate /* get the reply */ 61007c478bd9Sstevel@tonic-gate smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 61017c478bd9Sstevel@tonic-gate XS_STARTTLS); 61027c478bd9Sstevel@tonic-gate 61037c478bd9Sstevel@tonic-gate /* check return code from server */ 6104445f2479Sjbeck if (REPLYTYPE(smtpresult) == 4) 61057c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 61067c478bd9Sstevel@tonic-gate if (smtpresult == 501) 61077c478bd9Sstevel@tonic-gate return EX_USAGE; 61087c478bd9Sstevel@tonic-gate if (smtpresult == -1) 61097c478bd9Sstevel@tonic-gate return smtpresult; 6110445f2479Sjbeck 6111445f2479Sjbeck /* not an expected reply but we have to deal with it */ 6112445f2479Sjbeck if (REPLYTYPE(smtpresult) == 5) 6113445f2479Sjbeck return EX_UNAVAILABLE; 61147c478bd9Sstevel@tonic-gate if (smtpresult != 220) 61157c478bd9Sstevel@tonic-gate return EX_PROTOCOL; 61167c478bd9Sstevel@tonic-gate 61177c478bd9Sstevel@tonic-gate if (LogLevel > 13) 61187c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 61197c478bd9Sstevel@tonic-gate 61207c478bd9Sstevel@tonic-gate /* start connection */ 61217c478bd9Sstevel@tonic-gate if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 61227c478bd9Sstevel@tonic-gate { 61237c478bd9Sstevel@tonic-gate if (LogLevel > 5) 61247c478bd9Sstevel@tonic-gate { 61257c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 61267c478bd9Sstevel@tonic-gate "STARTTLS=client, error: SSL_new failed"); 61277c478bd9Sstevel@tonic-gate if (LogLevel > 9) 61287c478bd9Sstevel@tonic-gate tlslogerr("client"); 61297c478bd9Sstevel@tonic-gate } 61307c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 61317c478bd9Sstevel@tonic-gate } 61327c478bd9Sstevel@tonic-gate 61337c478bd9Sstevel@tonic-gate rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 61347c478bd9Sstevel@tonic-gate wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 61357c478bd9Sstevel@tonic-gate 61367c478bd9Sstevel@tonic-gate /* SSL_clear(clt_ssl); ? */ 61377c478bd9Sstevel@tonic-gate if (rfd < 0 || wfd < 0 || 61387c478bd9Sstevel@tonic-gate (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 61397c478bd9Sstevel@tonic-gate (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 61407c478bd9Sstevel@tonic-gate { 61417c478bd9Sstevel@tonic-gate if (LogLevel > 5) 61427c478bd9Sstevel@tonic-gate { 61437c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 61447c478bd9Sstevel@tonic-gate "STARTTLS=client, error: SSL_set_xfd failed=%d", 61457c478bd9Sstevel@tonic-gate result); 61467c478bd9Sstevel@tonic-gate if (LogLevel > 9) 61477c478bd9Sstevel@tonic-gate tlslogerr("client"); 61487c478bd9Sstevel@tonic-gate } 61497c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 61507c478bd9Sstevel@tonic-gate } 61517c478bd9Sstevel@tonic-gate SSL_set_connect_state(clt_ssl); 61527c478bd9Sstevel@tonic-gate tlsstart = curtime(); 61537c478bd9Sstevel@tonic-gate 61547c478bd9Sstevel@tonic-gate ssl_retry: 61557c478bd9Sstevel@tonic-gate if ((result = SSL_connect(clt_ssl)) <= 0) 61567c478bd9Sstevel@tonic-gate { 6157445f2479Sjbeck int i, ssl_err; 61587c478bd9Sstevel@tonic-gate 6159445f2479Sjbeck ssl_err = SSL_get_error(clt_ssl, result); 6160445f2479Sjbeck i = tls_retry(clt_ssl, rfd, wfd, tlsstart, 6161445f2479Sjbeck TimeOuts.to_starttls, ssl_err, "client"); 6162445f2479Sjbeck if (i > 0) 6163445f2479Sjbeck goto ssl_retry; 61647c478bd9Sstevel@tonic-gate 61657c478bd9Sstevel@tonic-gate if (LogLevel > 5) 61667c478bd9Sstevel@tonic-gate { 6167445f2479Sjbeck sm_syslog(LOG_WARNING, NOQID, 6168445f2479Sjbeck "STARTTLS=client, error: connect failed=%d, SSL_error=%d, errno=%d, retry=%d", 6169445f2479Sjbeck result, ssl_err, errno, i); 61707c478bd9Sstevel@tonic-gate if (LogLevel > 8) 61717c478bd9Sstevel@tonic-gate tlslogerr("client"); 61727c478bd9Sstevel@tonic-gate } 61737c478bd9Sstevel@tonic-gate 61747c478bd9Sstevel@tonic-gate SSL_free(clt_ssl); 61757c478bd9Sstevel@tonic-gate clt_ssl = NULL; 61767c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 61777c478bd9Sstevel@tonic-gate } 61787c478bd9Sstevel@tonic-gate mci->mci_ssl = clt_ssl; 61797c478bd9Sstevel@tonic-gate result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 61807c478bd9Sstevel@tonic-gate &mci->mci_macro, true); 61817c478bd9Sstevel@tonic-gate 61827c478bd9Sstevel@tonic-gate /* switch to use TLS... */ 61837c478bd9Sstevel@tonic-gate if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 61847c478bd9Sstevel@tonic-gate return EX_OK; 61857c478bd9Sstevel@tonic-gate 61867c478bd9Sstevel@tonic-gate /* failure */ 61877c478bd9Sstevel@tonic-gate SSL_free(clt_ssl); 61887c478bd9Sstevel@tonic-gate clt_ssl = NULL; 61897c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 61907c478bd9Sstevel@tonic-gate } 61917c478bd9Sstevel@tonic-gate /* 61927c478bd9Sstevel@tonic-gate ** ENDTLSCLT -- shutdown secure connection (client side) 61937c478bd9Sstevel@tonic-gate ** 61947c478bd9Sstevel@tonic-gate ** Parameters: 61957c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 61967c478bd9Sstevel@tonic-gate ** 61977c478bd9Sstevel@tonic-gate ** Returns: 61987c478bd9Sstevel@tonic-gate ** success? 61997c478bd9Sstevel@tonic-gate */ 62007c478bd9Sstevel@tonic-gate 62017c478bd9Sstevel@tonic-gate static int 62027c478bd9Sstevel@tonic-gate endtlsclt(mci) 62037c478bd9Sstevel@tonic-gate MCI *mci; 62047c478bd9Sstevel@tonic-gate { 62057c478bd9Sstevel@tonic-gate int r; 62067c478bd9Sstevel@tonic-gate 62077c478bd9Sstevel@tonic-gate if (!bitset(MCIF_TLSACT, mci->mci_flags)) 62087c478bd9Sstevel@tonic-gate return EX_OK; 62097c478bd9Sstevel@tonic-gate r = endtls(mci->mci_ssl, "client"); 62107c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLSACT; 62117c478bd9Sstevel@tonic-gate return r; 62127c478bd9Sstevel@tonic-gate } 62137c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 62147c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 62157c478bd9Sstevel@tonic-gate /* 62167c478bd9Sstevel@tonic-gate ** ISCLTFLGSET -- check whether client flag is set. 62177c478bd9Sstevel@tonic-gate ** 62187c478bd9Sstevel@tonic-gate ** Parameters: 62197c478bd9Sstevel@tonic-gate ** e -- envelope. 62207c478bd9Sstevel@tonic-gate ** flag -- flag to check in {client_flags} 62217c478bd9Sstevel@tonic-gate ** 62227c478bd9Sstevel@tonic-gate ** Returns: 62237c478bd9Sstevel@tonic-gate ** true iff flag is set. 62247c478bd9Sstevel@tonic-gate */ 62257c478bd9Sstevel@tonic-gate 62267c478bd9Sstevel@tonic-gate static bool 62277c478bd9Sstevel@tonic-gate iscltflgset(e, flag) 62287c478bd9Sstevel@tonic-gate ENVELOPE *e; 62297c478bd9Sstevel@tonic-gate int flag; 62307c478bd9Sstevel@tonic-gate { 62317c478bd9Sstevel@tonic-gate char *p; 62327c478bd9Sstevel@tonic-gate 62337c478bd9Sstevel@tonic-gate p = macvalue(macid("{client_flags}"), e); 62347c478bd9Sstevel@tonic-gate if (p == NULL) 62357c478bd9Sstevel@tonic-gate return false; 62367c478bd9Sstevel@tonic-gate for (; *p != '\0'; p++) 62377c478bd9Sstevel@tonic-gate { 62387c478bd9Sstevel@tonic-gate /* look for just this one flag */ 62397c478bd9Sstevel@tonic-gate if (*p == (char) flag) 62407c478bd9Sstevel@tonic-gate return true; 62417c478bd9Sstevel@tonic-gate } 62427c478bd9Sstevel@tonic-gate return false; 62437c478bd9Sstevel@tonic-gate } 62447c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 6245