1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers. 3*7c478bd9Sstevel@tonic-gate * All rights reserved. 4*7c478bd9Sstevel@tonic-gate * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5*7c478bd9Sstevel@tonic-gate * Copyright (c) 1988, 1993 6*7c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 7*7c478bd9Sstevel@tonic-gate * 8*7c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 9*7c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 10*7c478bd9Sstevel@tonic-gate * the sendmail distribution. 11*7c478bd9Sstevel@tonic-gate * 12*7c478bd9Sstevel@tonic-gate */ 13*7c478bd9Sstevel@tonic-gate 14*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 15*7c478bd9Sstevel@tonic-gate 16*7c478bd9Sstevel@tonic-gate #include <sendmail.h> 17*7c478bd9Sstevel@tonic-gate #include <sys/time.h> 18*7c478bd9Sstevel@tonic-gate 19*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: deliver.c,v 8.986 2005/03/05 02:28:50 ca Exp $") 20*7c478bd9Sstevel@tonic-gate 21*7c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT 22*7c478bd9Sstevel@tonic-gate # include <login_cap.h> 23*7c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */ 24*7c478bd9Sstevel@tonic-gate 25*7c478bd9Sstevel@tonic-gate #if NETINET || NETINET6 26*7c478bd9Sstevel@tonic-gate # include <arpa/inet.h> 27*7c478bd9Sstevel@tonic-gate #endif /* NETINET || NETINET6 */ 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #if STARTTLS || SASL 30*7c478bd9Sstevel@tonic-gate # include "sfsasl.h" 31*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS || SASL */ 32*7c478bd9Sstevel@tonic-gate 33*7c478bd9Sstevel@tonic-gate static int deliver __P((ENVELOPE *, ADDRESS *)); 34*7c478bd9Sstevel@tonic-gate static void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); 35*7c478bd9Sstevel@tonic-gate static void mailfiletimeout __P((int)); 36*7c478bd9Sstevel@tonic-gate static void endwaittimeout __P((int)); 37*7c478bd9Sstevel@tonic-gate static int parse_hostsignature __P((char *, char **, MAILER *)); 38*7c478bd9Sstevel@tonic-gate static void sendenvelope __P((ENVELOPE *, int)); 39*7c478bd9Sstevel@tonic-gate extern MCI *mci_new __P((SM_RPOOL_T *)); 40*7c478bd9Sstevel@tonic-gate static int coloncmp __P((const char *, const char *)); 41*7c478bd9Sstevel@tonic-gate 42*7c478bd9Sstevel@tonic-gate #if STARTTLS 43*7c478bd9Sstevel@tonic-gate static int starttls __P((MAILER *, MCI *, ENVELOPE *)); 44*7c478bd9Sstevel@tonic-gate static int endtlsclt __P((MCI *)); 45*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */ 46*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 47*7c478bd9Sstevel@tonic-gate static bool iscltflgset __P((ENVELOPE *, int)); 48*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 49*7c478bd9Sstevel@tonic-gate 50*7c478bd9Sstevel@tonic-gate /* 51*7c478bd9Sstevel@tonic-gate ** SENDALL -- actually send all the messages. 52*7c478bd9Sstevel@tonic-gate ** 53*7c478bd9Sstevel@tonic-gate ** Parameters: 54*7c478bd9Sstevel@tonic-gate ** e -- the envelope to send. 55*7c478bd9Sstevel@tonic-gate ** mode -- the delivery mode to use. If SM_DEFAULT, use 56*7c478bd9Sstevel@tonic-gate ** the current e->e_sendmode. 57*7c478bd9Sstevel@tonic-gate ** 58*7c478bd9Sstevel@tonic-gate ** Returns: 59*7c478bd9Sstevel@tonic-gate ** none. 60*7c478bd9Sstevel@tonic-gate ** 61*7c478bd9Sstevel@tonic-gate ** Side Effects: 62*7c478bd9Sstevel@tonic-gate ** Scans the send lists and sends everything it finds. 63*7c478bd9Sstevel@tonic-gate ** Delivers any appropriate error messages. 64*7c478bd9Sstevel@tonic-gate ** If we are running in a non-interactive mode, takes the 65*7c478bd9Sstevel@tonic-gate ** appropriate action. 66*7c478bd9Sstevel@tonic-gate */ 67*7c478bd9Sstevel@tonic-gate 68*7c478bd9Sstevel@tonic-gate void 69*7c478bd9Sstevel@tonic-gate sendall(e, mode) 70*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 71*7c478bd9Sstevel@tonic-gate int mode; 72*7c478bd9Sstevel@tonic-gate { 73*7c478bd9Sstevel@tonic-gate register ADDRESS *q; 74*7c478bd9Sstevel@tonic-gate char *owner; 75*7c478bd9Sstevel@tonic-gate int otherowners; 76*7c478bd9Sstevel@tonic-gate int save_errno; 77*7c478bd9Sstevel@tonic-gate register ENVELOPE *ee; 78*7c478bd9Sstevel@tonic-gate ENVELOPE *splitenv = NULL; 79*7c478bd9Sstevel@tonic-gate int oldverbose = Verbose; 80*7c478bd9Sstevel@tonic-gate bool somedeliveries = false, expensive = false; 81*7c478bd9Sstevel@tonic-gate pid_t pid; 82*7c478bd9Sstevel@tonic-gate 83*7c478bd9Sstevel@tonic-gate /* 84*7c478bd9Sstevel@tonic-gate ** If this message is to be discarded, don't bother sending 85*7c478bd9Sstevel@tonic-gate ** the message at all. 86*7c478bd9Sstevel@tonic-gate */ 87*7c478bd9Sstevel@tonic-gate 88*7c478bd9Sstevel@tonic-gate if (bitset(EF_DISCARD, e->e_flags)) 89*7c478bd9Sstevel@tonic-gate { 90*7c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 91*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall: discarding id %s\n", e->e_id); 92*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 93*7c478bd9Sstevel@tonic-gate if (LogLevel > 9) 94*7c478bd9Sstevel@tonic-gate logundelrcpts(e, "discarded", 9, true); 95*7c478bd9Sstevel@tonic-gate else if (LogLevel > 4) 96*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "discarded"); 97*7c478bd9Sstevel@tonic-gate markstats(e, NULL, STATS_REJECT); 98*7c478bd9Sstevel@tonic-gate return; 99*7c478bd9Sstevel@tonic-gate } 100*7c478bd9Sstevel@tonic-gate 101*7c478bd9Sstevel@tonic-gate /* 102*7c478bd9Sstevel@tonic-gate ** If we have had global, fatal errors, don't bother sending 103*7c478bd9Sstevel@tonic-gate ** the message at all if we are in SMTP mode. Local errors 104*7c478bd9Sstevel@tonic-gate ** (e.g., a single address failing) will still cause the other 105*7c478bd9Sstevel@tonic-gate ** addresses to be sent. 106*7c478bd9Sstevel@tonic-gate */ 107*7c478bd9Sstevel@tonic-gate 108*7c478bd9Sstevel@tonic-gate if (bitset(EF_FATALERRS, e->e_flags) && 109*7c478bd9Sstevel@tonic-gate (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 110*7c478bd9Sstevel@tonic-gate { 111*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 112*7c478bd9Sstevel@tonic-gate return; 113*7c478bd9Sstevel@tonic-gate } 114*7c478bd9Sstevel@tonic-gate 115*7c478bd9Sstevel@tonic-gate /* determine actual delivery mode */ 116*7c478bd9Sstevel@tonic-gate if (mode == SM_DEFAULT) 117*7c478bd9Sstevel@tonic-gate { 118*7c478bd9Sstevel@tonic-gate mode = e->e_sendmode; 119*7c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && mode != SM_DEFER && 120*7c478bd9Sstevel@tonic-gate shouldqueue(e->e_msgpriority, e->e_ctime)) 121*7c478bd9Sstevel@tonic-gate mode = SM_QUEUE; 122*7c478bd9Sstevel@tonic-gate } 123*7c478bd9Sstevel@tonic-gate 124*7c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 125*7c478bd9Sstevel@tonic-gate { 126*7c478bd9Sstevel@tonic-gate sm_dprintf("\n===== SENDALL: mode %c, id %s, e_from ", 127*7c478bd9Sstevel@tonic-gate mode, e->e_id); 128*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 129*7c478bd9Sstevel@tonic-gate sm_dprintf("\te_flags = "); 130*7c478bd9Sstevel@tonic-gate printenvflags(e); 131*7c478bd9Sstevel@tonic-gate sm_dprintf("sendqueue:\n"); 132*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 133*7c478bd9Sstevel@tonic-gate } 134*7c478bd9Sstevel@tonic-gate 135*7c478bd9Sstevel@tonic-gate /* 136*7c478bd9Sstevel@tonic-gate ** Do any preprocessing necessary for the mode we are running. 137*7c478bd9Sstevel@tonic-gate ** Check to make sure the hop count is reasonable. 138*7c478bd9Sstevel@tonic-gate ** Delete sends to the sender in mailing lists. 139*7c478bd9Sstevel@tonic-gate */ 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate CurEnv = e; 142*7c478bd9Sstevel@tonic-gate if (tTd(62, 1)) 143*7c478bd9Sstevel@tonic-gate checkfds(NULL); 144*7c478bd9Sstevel@tonic-gate 145*7c478bd9Sstevel@tonic-gate if (e->e_hopcount > MaxHopCount) 146*7c478bd9Sstevel@tonic-gate { 147*7c478bd9Sstevel@tonic-gate char *recip; 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate if (e->e_sendqueue != NULL && 150*7c478bd9Sstevel@tonic-gate e->e_sendqueue->q_paddr != NULL) 151*7c478bd9Sstevel@tonic-gate recip = e->e_sendqueue->q_paddr; 152*7c478bd9Sstevel@tonic-gate else 153*7c478bd9Sstevel@tonic-gate recip = "(nobody)"; 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate errno = 0; 156*7c478bd9Sstevel@tonic-gate queueup(e, WILL_BE_QUEUED(mode), false); 157*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; 158*7c478bd9Sstevel@tonic-gate ExitStat = EX_UNAVAILABLE; 159*7c478bd9Sstevel@tonic-gate syserr("554 5.4.6 Too many hops %d (%d max): from %s via %s, to %s", 160*7c478bd9Sstevel@tonic-gate e->e_hopcount, MaxHopCount, e->e_from.q_paddr, 161*7c478bd9Sstevel@tonic-gate RealHostName == NULL ? "localhost" : RealHostName, 162*7c478bd9Sstevel@tonic-gate recip); 163*7c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 164*7c478bd9Sstevel@tonic-gate { 165*7c478bd9Sstevel@tonic-gate if (QS_IS_DEAD(q->q_state)) 166*7c478bd9Sstevel@tonic-gate continue; 167*7c478bd9Sstevel@tonic-gate q->q_state = QS_BADADDR; 168*7c478bd9Sstevel@tonic-gate q->q_status = "5.4.6"; 169*7c478bd9Sstevel@tonic-gate q->q_rstatus = "554 5.4.6 Too many hops"; 170*7c478bd9Sstevel@tonic-gate } 171*7c478bd9Sstevel@tonic-gate return; 172*7c478bd9Sstevel@tonic-gate } 173*7c478bd9Sstevel@tonic-gate 174*7c478bd9Sstevel@tonic-gate /* 175*7c478bd9Sstevel@tonic-gate ** Do sender deletion. 176*7c478bd9Sstevel@tonic-gate ** 177*7c478bd9Sstevel@tonic-gate ** If the sender should be queued up, skip this. 178*7c478bd9Sstevel@tonic-gate ** This can happen if the name server is hosed when you 179*7c478bd9Sstevel@tonic-gate ** are trying to send mail. The result is that the sender 180*7c478bd9Sstevel@tonic-gate ** is instantiated in the queue as a recipient. 181*7c478bd9Sstevel@tonic-gate */ 182*7c478bd9Sstevel@tonic-gate 183*7c478bd9Sstevel@tonic-gate if (!bitset(EF_METOO, e->e_flags) && 184*7c478bd9Sstevel@tonic-gate !QS_IS_QUEUEUP(e->e_from.q_state)) 185*7c478bd9Sstevel@tonic-gate { 186*7c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 187*7c478bd9Sstevel@tonic-gate { 188*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall: QS_SENDER "); 189*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 190*7c478bd9Sstevel@tonic-gate } 191*7c478bd9Sstevel@tonic-gate e->e_from.q_state = QS_SENDER; 192*7c478bd9Sstevel@tonic-gate (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); 193*7c478bd9Sstevel@tonic-gate } 194*7c478bd9Sstevel@tonic-gate 195*7c478bd9Sstevel@tonic-gate /* 196*7c478bd9Sstevel@tonic-gate ** Handle alias owners. 197*7c478bd9Sstevel@tonic-gate ** 198*7c478bd9Sstevel@tonic-gate ** We scan up the q_alias chain looking for owners. 199*7c478bd9Sstevel@tonic-gate ** We discard owners that are the same as the return path. 200*7c478bd9Sstevel@tonic-gate */ 201*7c478bd9Sstevel@tonic-gate 202*7c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 203*7c478bd9Sstevel@tonic-gate { 204*7c478bd9Sstevel@tonic-gate register struct address *a; 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) 207*7c478bd9Sstevel@tonic-gate continue; 208*7c478bd9Sstevel@tonic-gate if (a != NULL) 209*7c478bd9Sstevel@tonic-gate q->q_owner = a->q_owner; 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate if (q->q_owner != NULL && 212*7c478bd9Sstevel@tonic-gate !QS_IS_DEAD(q->q_state) && 213*7c478bd9Sstevel@tonic-gate strcmp(q->q_owner, e->e_from.q_paddr) == 0) 214*7c478bd9Sstevel@tonic-gate q->q_owner = NULL; 215*7c478bd9Sstevel@tonic-gate } 216*7c478bd9Sstevel@tonic-gate 217*7c478bd9Sstevel@tonic-gate if (tTd(13, 25)) 218*7c478bd9Sstevel@tonic-gate { 219*7c478bd9Sstevel@tonic-gate sm_dprintf("\nAfter first owner pass, sendq =\n"); 220*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 221*7c478bd9Sstevel@tonic-gate } 222*7c478bd9Sstevel@tonic-gate 223*7c478bd9Sstevel@tonic-gate owner = ""; 224*7c478bd9Sstevel@tonic-gate otherowners = 1; 225*7c478bd9Sstevel@tonic-gate while (owner != NULL && otherowners > 0) 226*7c478bd9Sstevel@tonic-gate { 227*7c478bd9Sstevel@tonic-gate if (tTd(13, 28)) 228*7c478bd9Sstevel@tonic-gate sm_dprintf("owner = \"%s\", otherowners = %d\n", 229*7c478bd9Sstevel@tonic-gate owner, otherowners); 230*7c478bd9Sstevel@tonic-gate owner = NULL; 231*7c478bd9Sstevel@tonic-gate otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; 232*7c478bd9Sstevel@tonic-gate 233*7c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 234*7c478bd9Sstevel@tonic-gate { 235*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 236*7c478bd9Sstevel@tonic-gate { 237*7c478bd9Sstevel@tonic-gate sm_dprintf("Checking "); 238*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), q, false); 239*7c478bd9Sstevel@tonic-gate } 240*7c478bd9Sstevel@tonic-gate if (QS_IS_DEAD(q->q_state)) 241*7c478bd9Sstevel@tonic-gate { 242*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 243*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_DEAD\n"); 244*7c478bd9Sstevel@tonic-gate continue; 245*7c478bd9Sstevel@tonic-gate } 246*7c478bd9Sstevel@tonic-gate if (tTd(13, 29) && !tTd(13, 30)) 247*7c478bd9Sstevel@tonic-gate { 248*7c478bd9Sstevel@tonic-gate sm_dprintf("Checking "); 249*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), q, false); 250*7c478bd9Sstevel@tonic-gate } 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate if (q->q_owner != NULL) 253*7c478bd9Sstevel@tonic-gate { 254*7c478bd9Sstevel@tonic-gate if (owner == NULL) 255*7c478bd9Sstevel@tonic-gate { 256*7c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 257*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... First owner = \"%s\"\n", 258*7c478bd9Sstevel@tonic-gate q->q_owner); 259*7c478bd9Sstevel@tonic-gate owner = q->q_owner; 260*7c478bd9Sstevel@tonic-gate } 261*7c478bd9Sstevel@tonic-gate else if (owner != q->q_owner) 262*7c478bd9Sstevel@tonic-gate { 263*7c478bd9Sstevel@tonic-gate if (strcmp(owner, q->q_owner) == 0) 264*7c478bd9Sstevel@tonic-gate { 265*7c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 266*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... Same owner = \"%s\"\n", 267*7c478bd9Sstevel@tonic-gate owner); 268*7c478bd9Sstevel@tonic-gate 269*7c478bd9Sstevel@tonic-gate /* make future comparisons cheap */ 270*7c478bd9Sstevel@tonic-gate q->q_owner = owner; 271*7c478bd9Sstevel@tonic-gate } 272*7c478bd9Sstevel@tonic-gate else 273*7c478bd9Sstevel@tonic-gate { 274*7c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 275*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... Another owner \"%s\"\n", 276*7c478bd9Sstevel@tonic-gate q->q_owner); 277*7c478bd9Sstevel@tonic-gate otherowners++; 278*7c478bd9Sstevel@tonic-gate } 279*7c478bd9Sstevel@tonic-gate owner = q->q_owner; 280*7c478bd9Sstevel@tonic-gate } 281*7c478bd9Sstevel@tonic-gate else if (tTd(13, 40)) 282*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... Same owner = \"%s\"\n", 283*7c478bd9Sstevel@tonic-gate owner); 284*7c478bd9Sstevel@tonic-gate } 285*7c478bd9Sstevel@tonic-gate else 286*7c478bd9Sstevel@tonic-gate { 287*7c478bd9Sstevel@tonic-gate if (tTd(13, 40)) 288*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... Null owner\n"); 289*7c478bd9Sstevel@tonic-gate otherowners++; 290*7c478bd9Sstevel@tonic-gate } 291*7c478bd9Sstevel@tonic-gate 292*7c478bd9Sstevel@tonic-gate if (QS_IS_BADADDR(q->q_state)) 293*7c478bd9Sstevel@tonic-gate { 294*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 295*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_BADADDR\n"); 296*7c478bd9Sstevel@tonic-gate continue; 297*7c478bd9Sstevel@tonic-gate } 298*7c478bd9Sstevel@tonic-gate 299*7c478bd9Sstevel@tonic-gate if (QS_IS_QUEUEUP(q->q_state)) 300*7c478bd9Sstevel@tonic-gate { 301*7c478bd9Sstevel@tonic-gate MAILER *m = q->q_mailer; 302*7c478bd9Sstevel@tonic-gate 303*7c478bd9Sstevel@tonic-gate /* 304*7c478bd9Sstevel@tonic-gate ** If we have temporary address failures 305*7c478bd9Sstevel@tonic-gate ** (e.g., dns failure) and a fallback MX is 306*7c478bd9Sstevel@tonic-gate ** set, send directly to the fallback MX host. 307*7c478bd9Sstevel@tonic-gate */ 308*7c478bd9Sstevel@tonic-gate 309*7c478bd9Sstevel@tonic-gate if (FallbackMX != NULL && 310*7c478bd9Sstevel@tonic-gate !wordinclass(FallbackMX, 'w') && 311*7c478bd9Sstevel@tonic-gate mode != SM_VERIFY && 312*7c478bd9Sstevel@tonic-gate !bitnset(M_NOMX, m->m_flags) && 313*7c478bd9Sstevel@tonic-gate strcmp(m->m_mailer, "[IPC]") == 0 && 314*7c478bd9Sstevel@tonic-gate m->m_argv[0] != NULL && 315*7c478bd9Sstevel@tonic-gate strcmp(m->m_argv[0], "TCP") == 0) 316*7c478bd9Sstevel@tonic-gate { 317*7c478bd9Sstevel@tonic-gate int len; 318*7c478bd9Sstevel@tonic-gate char *p; 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 321*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... FallbackMX\n"); 322*7c478bd9Sstevel@tonic-gate 323*7c478bd9Sstevel@tonic-gate len = strlen(FallbackMX) + 1; 324*7c478bd9Sstevel@tonic-gate p = sm_rpool_malloc_x(e->e_rpool, len); 325*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, FallbackMX, len); 326*7c478bd9Sstevel@tonic-gate q->q_state = QS_OK; 327*7c478bd9Sstevel@tonic-gate q->q_host = p; 328*7c478bd9Sstevel@tonic-gate } 329*7c478bd9Sstevel@tonic-gate else 330*7c478bd9Sstevel@tonic-gate { 331*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 332*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... QS_IS_QUEUEUP\n"); 333*7c478bd9Sstevel@tonic-gate continue; 334*7c478bd9Sstevel@tonic-gate } 335*7c478bd9Sstevel@tonic-gate } 336*7c478bd9Sstevel@tonic-gate 337*7c478bd9Sstevel@tonic-gate /* 338*7c478bd9Sstevel@tonic-gate ** If this mailer is expensive, and if we don't 339*7c478bd9Sstevel@tonic-gate ** want to make connections now, just mark these 340*7c478bd9Sstevel@tonic-gate ** addresses and return. This is useful if we 341*7c478bd9Sstevel@tonic-gate ** want to batch connections to reduce load. This 342*7c478bd9Sstevel@tonic-gate ** will cause the messages to be queued up, and a 343*7c478bd9Sstevel@tonic-gate ** daemon will come along to send the messages later. 344*7c478bd9Sstevel@tonic-gate */ 345*7c478bd9Sstevel@tonic-gate 346*7c478bd9Sstevel@tonic-gate if (NoConnect && !Verbose && 347*7c478bd9Sstevel@tonic-gate bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) 348*7c478bd9Sstevel@tonic-gate { 349*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 350*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... expensive\n"); 351*7c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 352*7c478bd9Sstevel@tonic-gate expensive = true; 353*7c478bd9Sstevel@tonic-gate } 354*7c478bd9Sstevel@tonic-gate else if (bitnset(M_HOLD, q->q_mailer->m_flags) && 355*7c478bd9Sstevel@tonic-gate QueueLimitId == NULL && 356*7c478bd9Sstevel@tonic-gate QueueLimitSender == NULL && 357*7c478bd9Sstevel@tonic-gate QueueLimitRecipient == NULL) 358*7c478bd9Sstevel@tonic-gate { 359*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 360*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... hold\n"); 361*7c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 362*7c478bd9Sstevel@tonic-gate expensive = true; 363*7c478bd9Sstevel@tonic-gate } 364*7c478bd9Sstevel@tonic-gate else if (QueueMode != QM_QUARANTINE && 365*7c478bd9Sstevel@tonic-gate e->e_quarmsg != NULL) 366*7c478bd9Sstevel@tonic-gate { 367*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 368*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... quarantine: %s\n", 369*7c478bd9Sstevel@tonic-gate e->e_quarmsg); 370*7c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 371*7c478bd9Sstevel@tonic-gate expensive = true; 372*7c478bd9Sstevel@tonic-gate } 373*7c478bd9Sstevel@tonic-gate else 374*7c478bd9Sstevel@tonic-gate { 375*7c478bd9Sstevel@tonic-gate if (tTd(13, 30)) 376*7c478bd9Sstevel@tonic-gate sm_dprintf(" ... deliverable\n"); 377*7c478bd9Sstevel@tonic-gate somedeliveries = true; 378*7c478bd9Sstevel@tonic-gate } 379*7c478bd9Sstevel@tonic-gate } 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate if (owner != NULL && otherowners > 0) 382*7c478bd9Sstevel@tonic-gate { 383*7c478bd9Sstevel@tonic-gate /* 384*7c478bd9Sstevel@tonic-gate ** Split this envelope into two. 385*7c478bd9Sstevel@tonic-gate */ 386*7c478bd9Sstevel@tonic-gate 387*7c478bd9Sstevel@tonic-gate ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, 388*7c478bd9Sstevel@tonic-gate sizeof *ee); 389*7c478bd9Sstevel@tonic-gate STRUCTCOPY(*e, *ee); 390*7c478bd9Sstevel@tonic-gate ee->e_message = NULL; 391*7c478bd9Sstevel@tonic-gate ee->e_id = NULL; 392*7c478bd9Sstevel@tonic-gate assign_queueid(ee); 393*7c478bd9Sstevel@tonic-gate 394*7c478bd9Sstevel@tonic-gate if (tTd(13, 1)) 395*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", 396*7c478bd9Sstevel@tonic-gate e->e_id, ee->e_id, owner, 397*7c478bd9Sstevel@tonic-gate otherowners); 398*7c478bd9Sstevel@tonic-gate 399*7c478bd9Sstevel@tonic-gate ee->e_header = copyheader(e->e_header, ee->e_rpool); 400*7c478bd9Sstevel@tonic-gate ee->e_sendqueue = copyqueue(e->e_sendqueue, 401*7c478bd9Sstevel@tonic-gate ee->e_rpool); 402*7c478bd9Sstevel@tonic-gate ee->e_errorqueue = copyqueue(e->e_errorqueue, 403*7c478bd9Sstevel@tonic-gate ee->e_rpool); 404*7c478bd9Sstevel@tonic-gate ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); 405*7c478bd9Sstevel@tonic-gate ee->e_flags |= EF_NORECEIPT; 406*7c478bd9Sstevel@tonic-gate setsender(owner, ee, NULL, '\0', true); 407*7c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 408*7c478bd9Sstevel@tonic-gate { 409*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall(split): QS_SENDER "); 410*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &ee->e_from, false); 411*7c478bd9Sstevel@tonic-gate } 412*7c478bd9Sstevel@tonic-gate ee->e_from.q_state = QS_SENDER; 413*7c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 414*7c478bd9Sstevel@tonic-gate ee->e_lockfp = NULL; 415*7c478bd9Sstevel@tonic-gate ee->e_xfp = NULL; 416*7c478bd9Sstevel@tonic-gate ee->e_qgrp = e->e_qgrp; 417*7c478bd9Sstevel@tonic-gate ee->e_qdir = e->e_qdir; 418*7c478bd9Sstevel@tonic-gate ee->e_errormode = EM_MAIL; 419*7c478bd9Sstevel@tonic-gate ee->e_sibling = splitenv; 420*7c478bd9Sstevel@tonic-gate ee->e_statmsg = NULL; 421*7c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 422*7c478bd9Sstevel@tonic-gate ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 423*7c478bd9Sstevel@tonic-gate e->e_quarmsg); 424*7c478bd9Sstevel@tonic-gate splitenv = ee; 425*7c478bd9Sstevel@tonic-gate 426*7c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 427*7c478bd9Sstevel@tonic-gate { 428*7c478bd9Sstevel@tonic-gate if (q->q_owner == owner) 429*7c478bd9Sstevel@tonic-gate { 430*7c478bd9Sstevel@tonic-gate q->q_state = QS_CLONED; 431*7c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 432*7c478bd9Sstevel@tonic-gate sm_dprintf("\t... stripping %s from original envelope\n", 433*7c478bd9Sstevel@tonic-gate q->q_paddr); 434*7c478bd9Sstevel@tonic-gate } 435*7c478bd9Sstevel@tonic-gate } 436*7c478bd9Sstevel@tonic-gate for (q = ee->e_sendqueue; q != NULL; q = q->q_next) 437*7c478bd9Sstevel@tonic-gate { 438*7c478bd9Sstevel@tonic-gate if (q->q_owner != owner) 439*7c478bd9Sstevel@tonic-gate { 440*7c478bd9Sstevel@tonic-gate q->q_state = QS_CLONED; 441*7c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 442*7c478bd9Sstevel@tonic-gate sm_dprintf("\t... dropping %s from cloned envelope\n", 443*7c478bd9Sstevel@tonic-gate q->q_paddr); 444*7c478bd9Sstevel@tonic-gate } 445*7c478bd9Sstevel@tonic-gate else 446*7c478bd9Sstevel@tonic-gate { 447*7c478bd9Sstevel@tonic-gate /* clear DSN parameters */ 448*7c478bd9Sstevel@tonic-gate q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); 449*7c478bd9Sstevel@tonic-gate q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; 450*7c478bd9Sstevel@tonic-gate if (tTd(13, 6)) 451*7c478bd9Sstevel@tonic-gate sm_dprintf("\t... moving %s to cloned envelope\n", 452*7c478bd9Sstevel@tonic-gate q->q_paddr); 453*7c478bd9Sstevel@tonic-gate } 454*7c478bd9Sstevel@tonic-gate } 455*7c478bd9Sstevel@tonic-gate 456*7c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) 457*7c478bd9Sstevel@tonic-gate dup_queue_file(e, ee, DATAFL_LETTER); 458*7c478bd9Sstevel@tonic-gate 459*7c478bd9Sstevel@tonic-gate /* 460*7c478bd9Sstevel@tonic-gate ** Give the split envelope access to the parent 461*7c478bd9Sstevel@tonic-gate ** transcript file for errors obtained while 462*7c478bd9Sstevel@tonic-gate ** processing the recipients (done before the 463*7c478bd9Sstevel@tonic-gate ** envelope splitting). 464*7c478bd9Sstevel@tonic-gate */ 465*7c478bd9Sstevel@tonic-gate 466*7c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 467*7c478bd9Sstevel@tonic-gate ee->e_xfp = sm_io_dup(e->e_xfp); 468*7c478bd9Sstevel@tonic-gate 469*7c478bd9Sstevel@tonic-gate /* failed to dup e->e_xfp, start a new transcript */ 470*7c478bd9Sstevel@tonic-gate if (ee->e_xfp == NULL) 471*7c478bd9Sstevel@tonic-gate openxscript(ee); 472*7c478bd9Sstevel@tonic-gate 473*7c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY && LogLevel > 4) 474*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 475*7c478bd9Sstevel@tonic-gate "%s: clone: owner=%s", 476*7c478bd9Sstevel@tonic-gate ee->e_id, owner); 477*7c478bd9Sstevel@tonic-gate } 478*7c478bd9Sstevel@tonic-gate } 479*7c478bd9Sstevel@tonic-gate 480*7c478bd9Sstevel@tonic-gate if (owner != NULL) 481*7c478bd9Sstevel@tonic-gate { 482*7c478bd9Sstevel@tonic-gate setsender(owner, e, NULL, '\0', true); 483*7c478bd9Sstevel@tonic-gate if (tTd(13, 5)) 484*7c478bd9Sstevel@tonic-gate { 485*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall(owner): QS_SENDER "); 486*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), &e->e_from, false); 487*7c478bd9Sstevel@tonic-gate } 488*7c478bd9Sstevel@tonic-gate e->e_from.q_state = QS_SENDER; 489*7c478bd9Sstevel@tonic-gate e->e_errormode = EM_MAIL; 490*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_NORECEIPT; 491*7c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_FATALERRS; 492*7c478bd9Sstevel@tonic-gate } 493*7c478bd9Sstevel@tonic-gate 494*7c478bd9Sstevel@tonic-gate /* if nothing to be delivered, just queue up everything */ 495*7c478bd9Sstevel@tonic-gate if (!somedeliveries && !WILL_BE_QUEUED(mode) && 496*7c478bd9Sstevel@tonic-gate mode != SM_VERIFY) 497*7c478bd9Sstevel@tonic-gate { 498*7c478bd9Sstevel@tonic-gate time_t now; 499*7c478bd9Sstevel@tonic-gate 500*7c478bd9Sstevel@tonic-gate if (tTd(13, 29)) 501*7c478bd9Sstevel@tonic-gate sm_dprintf("No deliveries: auto-queuing\n"); 502*7c478bd9Sstevel@tonic-gate mode = SM_QUEUE; 503*7c478bd9Sstevel@tonic-gate now = curtime(); 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate /* treat this as a delivery in terms of counting tries */ 506*7c478bd9Sstevel@tonic-gate e->e_dtime = now; 507*7c478bd9Sstevel@tonic-gate if (!expensive) 508*7c478bd9Sstevel@tonic-gate e->e_ntries++; 509*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 510*7c478bd9Sstevel@tonic-gate { 511*7c478bd9Sstevel@tonic-gate ee->e_dtime = now; 512*7c478bd9Sstevel@tonic-gate if (!expensive) 513*7c478bd9Sstevel@tonic-gate ee->e_ntries++; 514*7c478bd9Sstevel@tonic-gate } 515*7c478bd9Sstevel@tonic-gate } 516*7c478bd9Sstevel@tonic-gate 517*7c478bd9Sstevel@tonic-gate if ((WILL_BE_QUEUED(mode) || mode == SM_FORK || 518*7c478bd9Sstevel@tonic-gate (mode != SM_VERIFY && 519*7c478bd9Sstevel@tonic-gate (SuperSafe == SAFE_REALLY || 520*7c478bd9Sstevel@tonic-gate SuperSafe == SAFE_REALLY_POSTMILTER))) && 521*7c478bd9Sstevel@tonic-gate (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) 522*7c478bd9Sstevel@tonic-gate { 523*7c478bd9Sstevel@tonic-gate bool msync; 524*7c478bd9Sstevel@tonic-gate 525*7c478bd9Sstevel@tonic-gate /* 526*7c478bd9Sstevel@tonic-gate ** Be sure everything is instantiated in the queue. 527*7c478bd9Sstevel@tonic-gate ** Split envelopes first in case the machine crashes. 528*7c478bd9Sstevel@tonic-gate ** If the original were done first, we may lose 529*7c478bd9Sstevel@tonic-gate ** recipients. 530*7c478bd9Sstevel@tonic-gate */ 531*7c478bd9Sstevel@tonic-gate 532*7c478bd9Sstevel@tonic-gate #if !HASFLOCK 533*7c478bd9Sstevel@tonic-gate msync = false; 534*7c478bd9Sstevel@tonic-gate #else /* !HASFLOCK */ 535*7c478bd9Sstevel@tonic-gate msync = mode == SM_FORK; 536*7c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */ 537*7c478bd9Sstevel@tonic-gate 538*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 539*7c478bd9Sstevel@tonic-gate queueup(ee, WILL_BE_QUEUED(mode), msync); 540*7c478bd9Sstevel@tonic-gate queueup(e, WILL_BE_QUEUED(mode), msync); 541*7c478bd9Sstevel@tonic-gate } 542*7c478bd9Sstevel@tonic-gate 543*7c478bd9Sstevel@tonic-gate if (tTd(62, 10)) 544*7c478bd9Sstevel@tonic-gate checkfds("after envelope splitting"); 545*7c478bd9Sstevel@tonic-gate 546*7c478bd9Sstevel@tonic-gate /* 547*7c478bd9Sstevel@tonic-gate ** If we belong in background, fork now. 548*7c478bd9Sstevel@tonic-gate */ 549*7c478bd9Sstevel@tonic-gate 550*7c478bd9Sstevel@tonic-gate if (tTd(13, 20)) 551*7c478bd9Sstevel@tonic-gate { 552*7c478bd9Sstevel@tonic-gate sm_dprintf("sendall: final mode = %c\n", mode); 553*7c478bd9Sstevel@tonic-gate if (tTd(13, 21)) 554*7c478bd9Sstevel@tonic-gate { 555*7c478bd9Sstevel@tonic-gate sm_dprintf("\n================ Final Send Queue(s) =====================\n"); 556*7c478bd9Sstevel@tonic-gate sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 557*7c478bd9Sstevel@tonic-gate e->e_id, e->e_from.q_paddr); 558*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), e->e_sendqueue, true); 559*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 560*7c478bd9Sstevel@tonic-gate { 561*7c478bd9Sstevel@tonic-gate sm_dprintf("\n *** Envelope %s, e_from=%s ***\n", 562*7c478bd9Sstevel@tonic-gate ee->e_id, ee->e_from.q_paddr); 563*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ee->e_sendqueue, true); 564*7c478bd9Sstevel@tonic-gate } 565*7c478bd9Sstevel@tonic-gate sm_dprintf("==========================================================\n\n"); 566*7c478bd9Sstevel@tonic-gate } 567*7c478bd9Sstevel@tonic-gate } 568*7c478bd9Sstevel@tonic-gate switch (mode) 569*7c478bd9Sstevel@tonic-gate { 570*7c478bd9Sstevel@tonic-gate case SM_VERIFY: 571*7c478bd9Sstevel@tonic-gate Verbose = 2; 572*7c478bd9Sstevel@tonic-gate break; 573*7c478bd9Sstevel@tonic-gate 574*7c478bd9Sstevel@tonic-gate case SM_QUEUE: 575*7c478bd9Sstevel@tonic-gate case SM_DEFER: 576*7c478bd9Sstevel@tonic-gate #if HASFLOCK 577*7c478bd9Sstevel@tonic-gate queueonly: 578*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 579*7c478bd9Sstevel@tonic-gate if (e->e_nrcpts > 0) 580*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 581*7c478bd9Sstevel@tonic-gate dropenvelope(e, splitenv != NULL, true); 582*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 583*7c478bd9Sstevel@tonic-gate { 584*7c478bd9Sstevel@tonic-gate if (ee->e_nrcpts > 0) 585*7c478bd9Sstevel@tonic-gate ee->e_flags |= EF_INQUEUE; 586*7c478bd9Sstevel@tonic-gate dropenvelope(ee, false, true); 587*7c478bd9Sstevel@tonic-gate } 588*7c478bd9Sstevel@tonic-gate return; 589*7c478bd9Sstevel@tonic-gate 590*7c478bd9Sstevel@tonic-gate case SM_FORK: 591*7c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 592*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 593*7c478bd9Sstevel@tonic-gate 594*7c478bd9Sstevel@tonic-gate #if !HASFLOCK 595*7c478bd9Sstevel@tonic-gate /* 596*7c478bd9Sstevel@tonic-gate ** Since fcntl locking has the interesting semantic that 597*7c478bd9Sstevel@tonic-gate ** the lock is owned by a process, not by an open file 598*7c478bd9Sstevel@tonic-gate ** descriptor, we have to flush this to the queue, and 599*7c478bd9Sstevel@tonic-gate ** then restart from scratch in the child. 600*7c478bd9Sstevel@tonic-gate */ 601*7c478bd9Sstevel@tonic-gate 602*7c478bd9Sstevel@tonic-gate { 603*7c478bd9Sstevel@tonic-gate /* save id for future use */ 604*7c478bd9Sstevel@tonic-gate char *qid = e->e_id; 605*7c478bd9Sstevel@tonic-gate 606*7c478bd9Sstevel@tonic-gate /* now drop the envelope in the parent */ 607*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 608*7c478bd9Sstevel@tonic-gate dropenvelope(e, splitenv != NULL, false); 609*7c478bd9Sstevel@tonic-gate 610*7c478bd9Sstevel@tonic-gate /* arrange to reacquire lock after fork */ 611*7c478bd9Sstevel@tonic-gate e->e_id = qid; 612*7c478bd9Sstevel@tonic-gate } 613*7c478bd9Sstevel@tonic-gate 614*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 615*7c478bd9Sstevel@tonic-gate { 616*7c478bd9Sstevel@tonic-gate /* save id for future use */ 617*7c478bd9Sstevel@tonic-gate char *qid = ee->e_id; 618*7c478bd9Sstevel@tonic-gate 619*7c478bd9Sstevel@tonic-gate /* drop envelope in parent */ 620*7c478bd9Sstevel@tonic-gate ee->e_flags |= EF_INQUEUE; 621*7c478bd9Sstevel@tonic-gate dropenvelope(ee, false, false); 622*7c478bd9Sstevel@tonic-gate 623*7c478bd9Sstevel@tonic-gate /* and save qid for reacquisition */ 624*7c478bd9Sstevel@tonic-gate ee->e_id = qid; 625*7c478bd9Sstevel@tonic-gate } 626*7c478bd9Sstevel@tonic-gate 627*7c478bd9Sstevel@tonic-gate #endif /* !HASFLOCK */ 628*7c478bd9Sstevel@tonic-gate 629*7c478bd9Sstevel@tonic-gate /* 630*7c478bd9Sstevel@tonic-gate ** Since the delivery may happen in a child and the parent 631*7c478bd9Sstevel@tonic-gate ** does not wait, the parent may close the maps thereby 632*7c478bd9Sstevel@tonic-gate ** removing any shared memory used by the map. Therefore, 633*7c478bd9Sstevel@tonic-gate ** close the maps now so the child will dynamically open 634*7c478bd9Sstevel@tonic-gate ** them if necessary. 635*7c478bd9Sstevel@tonic-gate */ 636*7c478bd9Sstevel@tonic-gate 637*7c478bd9Sstevel@tonic-gate closemaps(false); 638*7c478bd9Sstevel@tonic-gate 639*7c478bd9Sstevel@tonic-gate pid = fork(); 640*7c478bd9Sstevel@tonic-gate if (pid < 0) 641*7c478bd9Sstevel@tonic-gate { 642*7c478bd9Sstevel@tonic-gate syserr("deliver: fork 1"); 643*7c478bd9Sstevel@tonic-gate #if HASFLOCK 644*7c478bd9Sstevel@tonic-gate goto queueonly; 645*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 646*7c478bd9Sstevel@tonic-gate e->e_id = NULL; 647*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 648*7c478bd9Sstevel@tonic-gate ee->e_id = NULL; 649*7c478bd9Sstevel@tonic-gate return; 650*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 651*7c478bd9Sstevel@tonic-gate } 652*7c478bd9Sstevel@tonic-gate else if (pid > 0) 653*7c478bd9Sstevel@tonic-gate { 654*7c478bd9Sstevel@tonic-gate #if HASFLOCK 655*7c478bd9Sstevel@tonic-gate /* be sure we leave the temp files to our child */ 656*7c478bd9Sstevel@tonic-gate /* close any random open files in the envelope */ 657*7c478bd9Sstevel@tonic-gate closexscript(e); 658*7c478bd9Sstevel@tonic-gate if (e->e_dfp != NULL) 659*7c478bd9Sstevel@tonic-gate (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 660*7c478bd9Sstevel@tonic-gate e->e_dfp = NULL; 661*7c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_HAS_DF; 662*7c478bd9Sstevel@tonic-gate 663*7c478bd9Sstevel@tonic-gate /* can't call unlockqueue to avoid unlink of xfp */ 664*7c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 665*7c478bd9Sstevel@tonic-gate (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 666*7c478bd9Sstevel@tonic-gate else 667*7c478bd9Sstevel@tonic-gate syserr("%s: sendall: null lockfp", e->e_id); 668*7c478bd9Sstevel@tonic-gate e->e_lockfp = NULL; 669*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 670*7c478bd9Sstevel@tonic-gate 671*7c478bd9Sstevel@tonic-gate /* make sure the parent doesn't own the envelope */ 672*7c478bd9Sstevel@tonic-gate e->e_id = NULL; 673*7c478bd9Sstevel@tonic-gate 674*7c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK 675*7c478bd9Sstevel@tonic-gate /* catch intermediate zombie */ 676*7c478bd9Sstevel@tonic-gate (void) waitfor(pid); 677*7c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */ 678*7c478bd9Sstevel@tonic-gate return; 679*7c478bd9Sstevel@tonic-gate } 680*7c478bd9Sstevel@tonic-gate 681*7c478bd9Sstevel@tonic-gate /* Reset global flags */ 682*7c478bd9Sstevel@tonic-gate RestartRequest = NULL; 683*7c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 684*7c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 685*7c478bd9Sstevel@tonic-gate PendingSignal = 0; 686*7c478bd9Sstevel@tonic-gate 687*7c478bd9Sstevel@tonic-gate /* 688*7c478bd9Sstevel@tonic-gate ** Initialize exception stack and default exception 689*7c478bd9Sstevel@tonic-gate ** handler for child process. 690*7c478bd9Sstevel@tonic-gate */ 691*7c478bd9Sstevel@tonic-gate 692*7c478bd9Sstevel@tonic-gate sm_exc_newthread(fatal_error); 693*7c478bd9Sstevel@tonic-gate 694*7c478bd9Sstevel@tonic-gate /* 695*7c478bd9Sstevel@tonic-gate ** Since we have accepted responsbility for the message, 696*7c478bd9Sstevel@tonic-gate ** change the SIGTERM handler. intsig() (the old handler) 697*7c478bd9Sstevel@tonic-gate ** would remove the envelope if this was a command line 698*7c478bd9Sstevel@tonic-gate ** message submission. 699*7c478bd9Sstevel@tonic-gate */ 700*7c478bd9Sstevel@tonic-gate 701*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 702*7c478bd9Sstevel@tonic-gate 703*7c478bd9Sstevel@tonic-gate #if USE_DOUBLE_FORK 704*7c478bd9Sstevel@tonic-gate /* double fork to avoid zombies */ 705*7c478bd9Sstevel@tonic-gate pid = fork(); 706*7c478bd9Sstevel@tonic-gate if (pid > 0) 707*7c478bd9Sstevel@tonic-gate exit(EX_OK); 708*7c478bd9Sstevel@tonic-gate save_errno = errno; 709*7c478bd9Sstevel@tonic-gate #endif /* USE_DOUBLE_FORK */ 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 712*7c478bd9Sstevel@tonic-gate 713*7c478bd9Sstevel@tonic-gate /* be sure we are immune from the terminal */ 714*7c478bd9Sstevel@tonic-gate disconnect(2, e); 715*7c478bd9Sstevel@tonic-gate clearstats(); 716*7c478bd9Sstevel@tonic-gate 717*7c478bd9Sstevel@tonic-gate /* prevent parent from waiting if there was an error */ 718*7c478bd9Sstevel@tonic-gate if (pid < 0) 719*7c478bd9Sstevel@tonic-gate { 720*7c478bd9Sstevel@tonic-gate errno = save_errno; 721*7c478bd9Sstevel@tonic-gate syserr("deliver: fork 2"); 722*7c478bd9Sstevel@tonic-gate #if HASFLOCK 723*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_INQUEUE; 724*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 725*7c478bd9Sstevel@tonic-gate e->e_id = NULL; 726*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 727*7c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 728*7c478bd9Sstevel@tonic-gate } 729*7c478bd9Sstevel@tonic-gate 730*7c478bd9Sstevel@tonic-gate /* be sure to give error messages in child */ 731*7c478bd9Sstevel@tonic-gate QuickAbort = false; 732*7c478bd9Sstevel@tonic-gate 733*7c478bd9Sstevel@tonic-gate /* 734*7c478bd9Sstevel@tonic-gate ** Close any cached connections. 735*7c478bd9Sstevel@tonic-gate ** 736*7c478bd9Sstevel@tonic-gate ** We don't send the QUIT protocol because the parent 737*7c478bd9Sstevel@tonic-gate ** still knows about the connection. 738*7c478bd9Sstevel@tonic-gate ** 739*7c478bd9Sstevel@tonic-gate ** This should only happen when delivering an error 740*7c478bd9Sstevel@tonic-gate ** message. 741*7c478bd9Sstevel@tonic-gate */ 742*7c478bd9Sstevel@tonic-gate 743*7c478bd9Sstevel@tonic-gate mci_flush(false, NULL); 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate #if HASFLOCK 746*7c478bd9Sstevel@tonic-gate break; 747*7c478bd9Sstevel@tonic-gate #else /* HASFLOCK */ 748*7c478bd9Sstevel@tonic-gate 749*7c478bd9Sstevel@tonic-gate /* 750*7c478bd9Sstevel@tonic-gate ** Now reacquire and run the various queue files. 751*7c478bd9Sstevel@tonic-gate */ 752*7c478bd9Sstevel@tonic-gate 753*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 754*7c478bd9Sstevel@tonic-gate { 755*7c478bd9Sstevel@tonic-gate ENVELOPE *sibling = ee->e_sibling; 756*7c478bd9Sstevel@tonic-gate 757*7c478bd9Sstevel@tonic-gate (void) dowork(ee->e_qgrp, ee->e_qdir, ee->e_id, 758*7c478bd9Sstevel@tonic-gate false, false, ee); 759*7c478bd9Sstevel@tonic-gate ee->e_sibling = sibling; 760*7c478bd9Sstevel@tonic-gate } 761*7c478bd9Sstevel@tonic-gate (void) dowork(e->e_qgrp, e->e_qdir, e->e_id, 762*7c478bd9Sstevel@tonic-gate false, false, e); 763*7c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 764*7c478bd9Sstevel@tonic-gate #endif /* HASFLOCK */ 765*7c478bd9Sstevel@tonic-gate } 766*7c478bd9Sstevel@tonic-gate 767*7c478bd9Sstevel@tonic-gate sendenvelope(e, mode); 768*7c478bd9Sstevel@tonic-gate dropenvelope(e, true, true); 769*7c478bd9Sstevel@tonic-gate for (ee = splitenv; ee != NULL; ee = ee->e_sibling) 770*7c478bd9Sstevel@tonic-gate { 771*7c478bd9Sstevel@tonic-gate CurEnv = ee; 772*7c478bd9Sstevel@tonic-gate if (mode != SM_VERIFY) 773*7c478bd9Sstevel@tonic-gate openxscript(ee); 774*7c478bd9Sstevel@tonic-gate sendenvelope(ee, mode); 775*7c478bd9Sstevel@tonic-gate dropenvelope(ee, true, true); 776*7c478bd9Sstevel@tonic-gate } 777*7c478bd9Sstevel@tonic-gate CurEnv = e; 778*7c478bd9Sstevel@tonic-gate 779*7c478bd9Sstevel@tonic-gate Verbose = oldverbose; 780*7c478bd9Sstevel@tonic-gate if (mode == SM_FORK) 781*7c478bd9Sstevel@tonic-gate finis(true, true, ExitStat); 782*7c478bd9Sstevel@tonic-gate } 783*7c478bd9Sstevel@tonic-gate 784*7c478bd9Sstevel@tonic-gate static void 785*7c478bd9Sstevel@tonic-gate sendenvelope(e, mode) 786*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 787*7c478bd9Sstevel@tonic-gate int mode; 788*7c478bd9Sstevel@tonic-gate { 789*7c478bd9Sstevel@tonic-gate register ADDRESS *q; 790*7c478bd9Sstevel@tonic-gate bool didany; 791*7c478bd9Sstevel@tonic-gate 792*7c478bd9Sstevel@tonic-gate if (tTd(13, 10)) 793*7c478bd9Sstevel@tonic-gate sm_dprintf("sendenvelope(%s) e_flags=0x%lx\n", 794*7c478bd9Sstevel@tonic-gate e->e_id == NULL ? "[NOQUEUE]" : e->e_id, 795*7c478bd9Sstevel@tonic-gate e->e_flags); 796*7c478bd9Sstevel@tonic-gate if (LogLevel > 80) 797*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_DEBUG, e->e_id, 798*7c478bd9Sstevel@tonic-gate "sendenvelope, flags=0x%lx", 799*7c478bd9Sstevel@tonic-gate e->e_flags); 800*7c478bd9Sstevel@tonic-gate 801*7c478bd9Sstevel@tonic-gate /* 802*7c478bd9Sstevel@tonic-gate ** If we have had global, fatal errors, don't bother sending 803*7c478bd9Sstevel@tonic-gate ** the message at all if we are in SMTP mode. Local errors 804*7c478bd9Sstevel@tonic-gate ** (e.g., a single address failing) will still cause the other 805*7c478bd9Sstevel@tonic-gate ** addresses to be sent. 806*7c478bd9Sstevel@tonic-gate */ 807*7c478bd9Sstevel@tonic-gate 808*7c478bd9Sstevel@tonic-gate if (bitset(EF_FATALERRS, e->e_flags) && 809*7c478bd9Sstevel@tonic-gate (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 810*7c478bd9Sstevel@tonic-gate { 811*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 812*7c478bd9Sstevel@tonic-gate return; 813*7c478bd9Sstevel@tonic-gate } 814*7c478bd9Sstevel@tonic-gate 815*7c478bd9Sstevel@tonic-gate /* 816*7c478bd9Sstevel@tonic-gate ** Don't attempt deliveries if we want to bounce now 817*7c478bd9Sstevel@tonic-gate ** or if deliver-by time is exceeded. 818*7c478bd9Sstevel@tonic-gate */ 819*7c478bd9Sstevel@tonic-gate 820*7c478bd9Sstevel@tonic-gate if (!bitset(EF_RESPONSE, e->e_flags) && 821*7c478bd9Sstevel@tonic-gate (TimeOuts.to_q_return[e->e_timeoutclass] == NOW || 822*7c478bd9Sstevel@tonic-gate (IS_DLVR_RETURN(e) && e->e_deliver_by > 0 && 823*7c478bd9Sstevel@tonic-gate curtime() > e->e_ctime + e->e_deliver_by))) 824*7c478bd9Sstevel@tonic-gate return; 825*7c478bd9Sstevel@tonic-gate 826*7c478bd9Sstevel@tonic-gate /* 827*7c478bd9Sstevel@tonic-gate ** Run through the list and send everything. 828*7c478bd9Sstevel@tonic-gate ** 829*7c478bd9Sstevel@tonic-gate ** Set EF_GLOBALERRS so that error messages during delivery 830*7c478bd9Sstevel@tonic-gate ** result in returned mail. 831*7c478bd9Sstevel@tonic-gate */ 832*7c478bd9Sstevel@tonic-gate 833*7c478bd9Sstevel@tonic-gate e->e_nsent = 0; 834*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_GLOBALERRS; 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{envid}"), e->e_envid); 837*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{bodytype}"), e->e_bodytype); 838*7c478bd9Sstevel@tonic-gate didany = false; 839*7c478bd9Sstevel@tonic-gate 840*7c478bd9Sstevel@tonic-gate if (!bitset(EF_SPLIT, e->e_flags)) 841*7c478bd9Sstevel@tonic-gate { 842*7c478bd9Sstevel@tonic-gate ENVELOPE *oldsib; 843*7c478bd9Sstevel@tonic-gate ENVELOPE *ee; 844*7c478bd9Sstevel@tonic-gate 845*7c478bd9Sstevel@tonic-gate /* 846*7c478bd9Sstevel@tonic-gate ** Save old sibling and set it to NULL to avoid 847*7c478bd9Sstevel@tonic-gate ** queueing up the same envelopes again. 848*7c478bd9Sstevel@tonic-gate ** This requires that envelopes in that list have 849*7c478bd9Sstevel@tonic-gate ** been take care of before (or at some other place). 850*7c478bd9Sstevel@tonic-gate */ 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate oldsib = e->e_sibling; 853*7c478bd9Sstevel@tonic-gate e->e_sibling = NULL; 854*7c478bd9Sstevel@tonic-gate if (!split_by_recipient(e) && 855*7c478bd9Sstevel@tonic-gate bitset(EF_FATALERRS, e->e_flags)) 856*7c478bd9Sstevel@tonic-gate { 857*7c478bd9Sstevel@tonic-gate if (OpMode == MD_SMTP || OpMode == MD_DAEMON) 858*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_CLRQUEUE; 859*7c478bd9Sstevel@tonic-gate return; 860*7c478bd9Sstevel@tonic-gate } 861*7c478bd9Sstevel@tonic-gate for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 862*7c478bd9Sstevel@tonic-gate queueup(ee, false, true); 863*7c478bd9Sstevel@tonic-gate 864*7c478bd9Sstevel@tonic-gate /* clean up */ 865*7c478bd9Sstevel@tonic-gate for (ee = e->e_sibling; ee != NULL; ee = ee->e_sibling) 866*7c478bd9Sstevel@tonic-gate { 867*7c478bd9Sstevel@tonic-gate /* now unlock the job */ 868*7c478bd9Sstevel@tonic-gate closexscript(ee); 869*7c478bd9Sstevel@tonic-gate unlockqueue(ee); 870*7c478bd9Sstevel@tonic-gate 871*7c478bd9Sstevel@tonic-gate /* this envelope is marked unused */ 872*7c478bd9Sstevel@tonic-gate if (ee->e_dfp != NULL) 873*7c478bd9Sstevel@tonic-gate { 874*7c478bd9Sstevel@tonic-gate (void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT); 875*7c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 876*7c478bd9Sstevel@tonic-gate } 877*7c478bd9Sstevel@tonic-gate ee->e_id = NULL; 878*7c478bd9Sstevel@tonic-gate ee->e_flags &= ~EF_HAS_DF; 879*7c478bd9Sstevel@tonic-gate } 880*7c478bd9Sstevel@tonic-gate e->e_sibling = oldsib; 881*7c478bd9Sstevel@tonic-gate } 882*7c478bd9Sstevel@tonic-gate 883*7c478bd9Sstevel@tonic-gate /* now run through the queue */ 884*7c478bd9Sstevel@tonic-gate for (q = e->e_sendqueue; q != NULL; q = q->q_next) 885*7c478bd9Sstevel@tonic-gate { 886*7c478bd9Sstevel@tonic-gate #if XDEBUG 887*7c478bd9Sstevel@tonic-gate char wbuf[MAXNAME + 20]; 888*7c478bd9Sstevel@tonic-gate 889*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", 890*7c478bd9Sstevel@tonic-gate MAXNAME, q->q_paddr); 891*7c478bd9Sstevel@tonic-gate checkfd012(wbuf); 892*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 893*7c478bd9Sstevel@tonic-gate if (mode == SM_VERIFY) 894*7c478bd9Sstevel@tonic-gate { 895*7c478bd9Sstevel@tonic-gate e->e_to = q->q_paddr; 896*7c478bd9Sstevel@tonic-gate if (QS_IS_SENDABLE(q->q_state)) 897*7c478bd9Sstevel@tonic-gate { 898*7c478bd9Sstevel@tonic-gate if (q->q_host != NULL && q->q_host[0] != '\0') 899*7c478bd9Sstevel@tonic-gate message("deliverable: mailer %s, host %s, user %s", 900*7c478bd9Sstevel@tonic-gate q->q_mailer->m_name, 901*7c478bd9Sstevel@tonic-gate q->q_host, 902*7c478bd9Sstevel@tonic-gate q->q_user); 903*7c478bd9Sstevel@tonic-gate else 904*7c478bd9Sstevel@tonic-gate message("deliverable: mailer %s, user %s", 905*7c478bd9Sstevel@tonic-gate q->q_mailer->m_name, 906*7c478bd9Sstevel@tonic-gate q->q_user); 907*7c478bd9Sstevel@tonic-gate } 908*7c478bd9Sstevel@tonic-gate } 909*7c478bd9Sstevel@tonic-gate else if (QS_IS_OK(q->q_state)) 910*7c478bd9Sstevel@tonic-gate { 911*7c478bd9Sstevel@tonic-gate /* 912*7c478bd9Sstevel@tonic-gate ** Checkpoint the send list every few addresses 913*7c478bd9Sstevel@tonic-gate */ 914*7c478bd9Sstevel@tonic-gate 915*7c478bd9Sstevel@tonic-gate if (CheckpointInterval > 0 && 916*7c478bd9Sstevel@tonic-gate e->e_nsent >= CheckpointInterval) 917*7c478bd9Sstevel@tonic-gate { 918*7c478bd9Sstevel@tonic-gate queueup(e, false, false); 919*7c478bd9Sstevel@tonic-gate e->e_nsent = 0; 920*7c478bd9Sstevel@tonic-gate } 921*7c478bd9Sstevel@tonic-gate (void) deliver(e, q); 922*7c478bd9Sstevel@tonic-gate didany = true; 923*7c478bd9Sstevel@tonic-gate } 924*7c478bd9Sstevel@tonic-gate } 925*7c478bd9Sstevel@tonic-gate if (didany) 926*7c478bd9Sstevel@tonic-gate { 927*7c478bd9Sstevel@tonic-gate e->e_dtime = curtime(); 928*7c478bd9Sstevel@tonic-gate e->e_ntries++; 929*7c478bd9Sstevel@tonic-gate } 930*7c478bd9Sstevel@tonic-gate 931*7c478bd9Sstevel@tonic-gate #if XDEBUG 932*7c478bd9Sstevel@tonic-gate checkfd012("end of sendenvelope"); 933*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 934*7c478bd9Sstevel@tonic-gate } 935*7c478bd9Sstevel@tonic-gate 936*7c478bd9Sstevel@tonic-gate #if REQUIRES_DIR_FSYNC 937*7c478bd9Sstevel@tonic-gate /* 938*7c478bd9Sstevel@tonic-gate ** SYNC_DIR -- fsync a directory based on a filename 939*7c478bd9Sstevel@tonic-gate ** 940*7c478bd9Sstevel@tonic-gate ** Parameters: 941*7c478bd9Sstevel@tonic-gate ** filename -- path of file 942*7c478bd9Sstevel@tonic-gate ** panic -- panic? 943*7c478bd9Sstevel@tonic-gate ** 944*7c478bd9Sstevel@tonic-gate ** Returns: 945*7c478bd9Sstevel@tonic-gate ** none 946*7c478bd9Sstevel@tonic-gate */ 947*7c478bd9Sstevel@tonic-gate 948*7c478bd9Sstevel@tonic-gate void 949*7c478bd9Sstevel@tonic-gate sync_dir(filename, panic) 950*7c478bd9Sstevel@tonic-gate char *filename; 951*7c478bd9Sstevel@tonic-gate bool panic; 952*7c478bd9Sstevel@tonic-gate { 953*7c478bd9Sstevel@tonic-gate int dirfd; 954*7c478bd9Sstevel@tonic-gate char *dirp; 955*7c478bd9Sstevel@tonic-gate char dir[MAXPATHLEN]; 956*7c478bd9Sstevel@tonic-gate 957*7c478bd9Sstevel@tonic-gate if (!RequiresDirfsync) 958*7c478bd9Sstevel@tonic-gate return; 959*7c478bd9Sstevel@tonic-gate 960*7c478bd9Sstevel@tonic-gate /* filesystems which require the directory be synced */ 961*7c478bd9Sstevel@tonic-gate dirp = strrchr(filename, '/'); 962*7c478bd9Sstevel@tonic-gate if (dirp != NULL) 963*7c478bd9Sstevel@tonic-gate { 964*7c478bd9Sstevel@tonic-gate if (sm_strlcpy(dir, filename, sizeof dir) >= sizeof dir) 965*7c478bd9Sstevel@tonic-gate return; 966*7c478bd9Sstevel@tonic-gate dir[dirp - filename] = '\0'; 967*7c478bd9Sstevel@tonic-gate dirp = dir; 968*7c478bd9Sstevel@tonic-gate } 969*7c478bd9Sstevel@tonic-gate else 970*7c478bd9Sstevel@tonic-gate dirp = "."; 971*7c478bd9Sstevel@tonic-gate dirfd = open(dirp, O_RDONLY, 0700); 972*7c478bd9Sstevel@tonic-gate if (tTd(40,32)) 973*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, "sync_dir: %s: fsync(%d)", 974*7c478bd9Sstevel@tonic-gate dirp, dirfd); 975*7c478bd9Sstevel@tonic-gate if (dirfd >= 0) 976*7c478bd9Sstevel@tonic-gate { 977*7c478bd9Sstevel@tonic-gate if (fsync(dirfd) < 0) 978*7c478bd9Sstevel@tonic-gate { 979*7c478bd9Sstevel@tonic-gate if (panic) 980*7c478bd9Sstevel@tonic-gate syserr("!sync_dir: cannot fsync directory %s", 981*7c478bd9Sstevel@tonic-gate dirp); 982*7c478bd9Sstevel@tonic-gate else if (LogLevel > 1) 983*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 984*7c478bd9Sstevel@tonic-gate "sync_dir: cannot fsync directory %s: %s", 985*7c478bd9Sstevel@tonic-gate dirp, sm_errstring(errno)); 986*7c478bd9Sstevel@tonic-gate } 987*7c478bd9Sstevel@tonic-gate (void) close(dirfd); 988*7c478bd9Sstevel@tonic-gate } 989*7c478bd9Sstevel@tonic-gate } 990*7c478bd9Sstevel@tonic-gate #endif /* REQUIRES_DIR_FSYNC */ 991*7c478bd9Sstevel@tonic-gate /* 992*7c478bd9Sstevel@tonic-gate ** DUP_QUEUE_FILE -- duplicate a queue file into a split queue 993*7c478bd9Sstevel@tonic-gate ** 994*7c478bd9Sstevel@tonic-gate ** Parameters: 995*7c478bd9Sstevel@tonic-gate ** e -- the existing envelope 996*7c478bd9Sstevel@tonic-gate ** ee -- the new envelope 997*7c478bd9Sstevel@tonic-gate ** type -- the queue file type (e.g., DATAFL_LETTER) 998*7c478bd9Sstevel@tonic-gate ** 999*7c478bd9Sstevel@tonic-gate ** Returns: 1000*7c478bd9Sstevel@tonic-gate ** none 1001*7c478bd9Sstevel@tonic-gate */ 1002*7c478bd9Sstevel@tonic-gate 1003*7c478bd9Sstevel@tonic-gate static void 1004*7c478bd9Sstevel@tonic-gate dup_queue_file(e, ee, type) 1005*7c478bd9Sstevel@tonic-gate ENVELOPE *e, *ee; 1006*7c478bd9Sstevel@tonic-gate int type; 1007*7c478bd9Sstevel@tonic-gate { 1008*7c478bd9Sstevel@tonic-gate char f1buf[MAXPATHLEN], f2buf[MAXPATHLEN]; 1009*7c478bd9Sstevel@tonic-gate 1010*7c478bd9Sstevel@tonic-gate ee->e_dfp = NULL; 1011*7c478bd9Sstevel@tonic-gate ee->e_xfp = NULL; 1012*7c478bd9Sstevel@tonic-gate 1013*7c478bd9Sstevel@tonic-gate /* 1014*7c478bd9Sstevel@tonic-gate ** Make sure both are in the same directory. 1015*7c478bd9Sstevel@tonic-gate */ 1016*7c478bd9Sstevel@tonic-gate 1017*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(f1buf, queuename(e, type), sizeof f1buf); 1018*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(f2buf, queuename(ee, type), sizeof f2buf); 1019*7c478bd9Sstevel@tonic-gate 1020*7c478bd9Sstevel@tonic-gate /* Force the df to disk if it's not there yet */ 1021*7c478bd9Sstevel@tonic-gate if (type == DATAFL_LETTER && e->e_dfp != NULL && 1022*7c478bd9Sstevel@tonic-gate sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 1023*7c478bd9Sstevel@tonic-gate errno != EINVAL) 1024*7c478bd9Sstevel@tonic-gate { 1025*7c478bd9Sstevel@tonic-gate syserr("!dup_queue_file: can't commit %s", f1buf); 1026*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1027*7c478bd9Sstevel@tonic-gate } 1028*7c478bd9Sstevel@tonic-gate 1029*7c478bd9Sstevel@tonic-gate if (link(f1buf, f2buf) < 0) 1030*7c478bd9Sstevel@tonic-gate { 1031*7c478bd9Sstevel@tonic-gate int save_errno = errno; 1032*7c478bd9Sstevel@tonic-gate 1033*7c478bd9Sstevel@tonic-gate syserr("sendall: link(%s, %s)", f1buf, f2buf); 1034*7c478bd9Sstevel@tonic-gate if (save_errno == EEXIST) 1035*7c478bd9Sstevel@tonic-gate { 1036*7c478bd9Sstevel@tonic-gate if (unlink(f2buf) < 0) 1037*7c478bd9Sstevel@tonic-gate { 1038*7c478bd9Sstevel@tonic-gate syserr("!sendall: unlink(%s): permanent", 1039*7c478bd9Sstevel@tonic-gate f2buf); 1040*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1041*7c478bd9Sstevel@tonic-gate } 1042*7c478bd9Sstevel@tonic-gate if (link(f1buf, f2buf) < 0) 1043*7c478bd9Sstevel@tonic-gate { 1044*7c478bd9Sstevel@tonic-gate syserr("!sendall: link(%s, %s): permanent", 1045*7c478bd9Sstevel@tonic-gate f1buf, f2buf); 1046*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 1047*7c478bd9Sstevel@tonic-gate } 1048*7c478bd9Sstevel@tonic-gate } 1049*7c478bd9Sstevel@tonic-gate } 1050*7c478bd9Sstevel@tonic-gate SYNC_DIR(f2buf, true); 1051*7c478bd9Sstevel@tonic-gate } 1052*7c478bd9Sstevel@tonic-gate /* 1053*7c478bd9Sstevel@tonic-gate ** DOFORK -- do a fork, retrying a couple of times on failure. 1054*7c478bd9Sstevel@tonic-gate ** 1055*7c478bd9Sstevel@tonic-gate ** This MUST be a macro, since after a vfork we are running 1056*7c478bd9Sstevel@tonic-gate ** two processes on the same stack!!! 1057*7c478bd9Sstevel@tonic-gate ** 1058*7c478bd9Sstevel@tonic-gate ** Parameters: 1059*7c478bd9Sstevel@tonic-gate ** none. 1060*7c478bd9Sstevel@tonic-gate ** 1061*7c478bd9Sstevel@tonic-gate ** Returns: 1062*7c478bd9Sstevel@tonic-gate ** From a macro??? You've got to be kidding! 1063*7c478bd9Sstevel@tonic-gate ** 1064*7c478bd9Sstevel@tonic-gate ** Side Effects: 1065*7c478bd9Sstevel@tonic-gate ** Modifies the ==> LOCAL <== variable 'pid', leaving: 1066*7c478bd9Sstevel@tonic-gate ** pid of child in parent, zero in child. 1067*7c478bd9Sstevel@tonic-gate ** -1 on unrecoverable error. 1068*7c478bd9Sstevel@tonic-gate ** 1069*7c478bd9Sstevel@tonic-gate ** Notes: 1070*7c478bd9Sstevel@tonic-gate ** I'm awfully sorry this looks so awful. That's 1071*7c478bd9Sstevel@tonic-gate ** vfork for you..... 1072*7c478bd9Sstevel@tonic-gate */ 1073*7c478bd9Sstevel@tonic-gate 1074*7c478bd9Sstevel@tonic-gate #define NFORKTRIES 5 1075*7c478bd9Sstevel@tonic-gate 1076*7c478bd9Sstevel@tonic-gate #ifndef FORK 1077*7c478bd9Sstevel@tonic-gate # define FORK fork 1078*7c478bd9Sstevel@tonic-gate #endif /* ! FORK */ 1079*7c478bd9Sstevel@tonic-gate 1080*7c478bd9Sstevel@tonic-gate #define DOFORK(fORKfN) \ 1081*7c478bd9Sstevel@tonic-gate {\ 1082*7c478bd9Sstevel@tonic-gate register int i;\ 1083*7c478bd9Sstevel@tonic-gate \ 1084*7c478bd9Sstevel@tonic-gate for (i = NFORKTRIES; --i >= 0; )\ 1085*7c478bd9Sstevel@tonic-gate {\ 1086*7c478bd9Sstevel@tonic-gate pid = fORKfN();\ 1087*7c478bd9Sstevel@tonic-gate if (pid >= 0)\ 1088*7c478bd9Sstevel@tonic-gate break;\ 1089*7c478bd9Sstevel@tonic-gate if (i > 0)\ 1090*7c478bd9Sstevel@tonic-gate (void) sleep((unsigned) NFORKTRIES - i);\ 1091*7c478bd9Sstevel@tonic-gate }\ 1092*7c478bd9Sstevel@tonic-gate } 1093*7c478bd9Sstevel@tonic-gate /* 1094*7c478bd9Sstevel@tonic-gate ** DOFORK -- simple fork interface to DOFORK. 1095*7c478bd9Sstevel@tonic-gate ** 1096*7c478bd9Sstevel@tonic-gate ** Parameters: 1097*7c478bd9Sstevel@tonic-gate ** none. 1098*7c478bd9Sstevel@tonic-gate ** 1099*7c478bd9Sstevel@tonic-gate ** Returns: 1100*7c478bd9Sstevel@tonic-gate ** pid of child in parent. 1101*7c478bd9Sstevel@tonic-gate ** zero in child. 1102*7c478bd9Sstevel@tonic-gate ** -1 on error. 1103*7c478bd9Sstevel@tonic-gate ** 1104*7c478bd9Sstevel@tonic-gate ** Side Effects: 1105*7c478bd9Sstevel@tonic-gate ** returns twice, once in parent and once in child. 1106*7c478bd9Sstevel@tonic-gate */ 1107*7c478bd9Sstevel@tonic-gate 1108*7c478bd9Sstevel@tonic-gate pid_t 1109*7c478bd9Sstevel@tonic-gate dofork() 1110*7c478bd9Sstevel@tonic-gate { 1111*7c478bd9Sstevel@tonic-gate register pid_t pid = -1; 1112*7c478bd9Sstevel@tonic-gate 1113*7c478bd9Sstevel@tonic-gate DOFORK(fork); 1114*7c478bd9Sstevel@tonic-gate return pid; 1115*7c478bd9Sstevel@tonic-gate } 1116*7c478bd9Sstevel@tonic-gate 1117*7c478bd9Sstevel@tonic-gate /* 1118*7c478bd9Sstevel@tonic-gate ** COLONCMP -- compare host-signatures up to first ':' or EOS 1119*7c478bd9Sstevel@tonic-gate ** 1120*7c478bd9Sstevel@tonic-gate ** This takes two strings which happen to be host-signatures and 1121*7c478bd9Sstevel@tonic-gate ** compares them. If the lowest preference portions of the MX-RR's 1122*7c478bd9Sstevel@tonic-gate ** match (up to ':' or EOS, whichever is first), then we have 1123*7c478bd9Sstevel@tonic-gate ** match. This is used for coattail-piggybacking messages during 1124*7c478bd9Sstevel@tonic-gate ** message delivery. 1125*7c478bd9Sstevel@tonic-gate ** If the signatures are the same up to the first ':' the remainder of 1126*7c478bd9Sstevel@tonic-gate ** the signatures are then compared with a normal strcmp(). This saves 1127*7c478bd9Sstevel@tonic-gate ** re-examining the first part of the signatures. 1128*7c478bd9Sstevel@tonic-gate ** 1129*7c478bd9Sstevel@tonic-gate ** Parameters: 1130*7c478bd9Sstevel@tonic-gate ** a - first host-signature 1131*7c478bd9Sstevel@tonic-gate ** b - second host-signature 1132*7c478bd9Sstevel@tonic-gate ** 1133*7c478bd9Sstevel@tonic-gate ** Returns: 1134*7c478bd9Sstevel@tonic-gate ** HS_MATCH_NO -- no "match". 1135*7c478bd9Sstevel@tonic-gate ** HS_MATCH_FIRST -- "match" for the first MX preference 1136*7c478bd9Sstevel@tonic-gate ** (up to the first colon (':')). 1137*7c478bd9Sstevel@tonic-gate ** HS_MATCH_FULL -- match for the entire MX record. 1138*7c478bd9Sstevel@tonic-gate ** 1139*7c478bd9Sstevel@tonic-gate ** Side Effects: 1140*7c478bd9Sstevel@tonic-gate ** none. 1141*7c478bd9Sstevel@tonic-gate */ 1142*7c478bd9Sstevel@tonic-gate 1143*7c478bd9Sstevel@tonic-gate #define HS_MATCH_NO 0 1144*7c478bd9Sstevel@tonic-gate #define HS_MATCH_FIRST 1 1145*7c478bd9Sstevel@tonic-gate #define HS_MATCH_FULL 2 1146*7c478bd9Sstevel@tonic-gate 1147*7c478bd9Sstevel@tonic-gate static int 1148*7c478bd9Sstevel@tonic-gate coloncmp(a, b) 1149*7c478bd9Sstevel@tonic-gate register const char *a; 1150*7c478bd9Sstevel@tonic-gate register const char *b; 1151*7c478bd9Sstevel@tonic-gate { 1152*7c478bd9Sstevel@tonic-gate int ret = HS_MATCH_NO; 1153*7c478bd9Sstevel@tonic-gate int braclev = 0; 1154*7c478bd9Sstevel@tonic-gate 1155*7c478bd9Sstevel@tonic-gate while (*a == *b++) 1156*7c478bd9Sstevel@tonic-gate { 1157*7c478bd9Sstevel@tonic-gate /* Need to account for IPv6 bracketed addresses */ 1158*7c478bd9Sstevel@tonic-gate if (*a == '[') 1159*7c478bd9Sstevel@tonic-gate braclev++; 1160*7c478bd9Sstevel@tonic-gate else if (*a == ']' && braclev > 0) 1161*7c478bd9Sstevel@tonic-gate braclev--; 1162*7c478bd9Sstevel@tonic-gate else if (*a == ':' && braclev <= 0) 1163*7c478bd9Sstevel@tonic-gate { 1164*7c478bd9Sstevel@tonic-gate ret = HS_MATCH_FIRST; 1165*7c478bd9Sstevel@tonic-gate a++; 1166*7c478bd9Sstevel@tonic-gate break; 1167*7c478bd9Sstevel@tonic-gate } 1168*7c478bd9Sstevel@tonic-gate else if (*a == '\0') 1169*7c478bd9Sstevel@tonic-gate return HS_MATCH_FULL; /* a full match */ 1170*7c478bd9Sstevel@tonic-gate a++; 1171*7c478bd9Sstevel@tonic-gate } 1172*7c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_NO && 1173*7c478bd9Sstevel@tonic-gate braclev <= 0 && 1174*7c478bd9Sstevel@tonic-gate ((*a == '\0' && *(b - 1) == ':') || 1175*7c478bd9Sstevel@tonic-gate (*a == ':' && *(b - 1) == '\0'))) 1176*7c478bd9Sstevel@tonic-gate return HS_MATCH_FIRST; 1177*7c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_FIRST && strcmp(a, b) == 0) 1178*7c478bd9Sstevel@tonic-gate return HS_MATCH_FULL; 1179*7c478bd9Sstevel@tonic-gate 1180*7c478bd9Sstevel@tonic-gate return ret; 1181*7c478bd9Sstevel@tonic-gate } 1182*7c478bd9Sstevel@tonic-gate 1183*7c478bd9Sstevel@tonic-gate /* 1184*7c478bd9Sstevel@tonic-gate ** SHOULD_TRY_FBSH -- Should try FallbackSmartHost? 1185*7c478bd9Sstevel@tonic-gate ** 1186*7c478bd9Sstevel@tonic-gate ** Parameters: 1187*7c478bd9Sstevel@tonic-gate ** e -- envelope 1188*7c478bd9Sstevel@tonic-gate ** tried_fallbacksmarthost -- has been tried already? (in/out) 1189*7c478bd9Sstevel@tonic-gate ** hostbuf -- buffer for hostname (expand FallbackSmartHost) (out) 1190*7c478bd9Sstevel@tonic-gate ** hbsz -- size of hostbuf 1191*7c478bd9Sstevel@tonic-gate ** status -- current delivery status 1192*7c478bd9Sstevel@tonic-gate ** 1193*7c478bd9Sstevel@tonic-gate ** Returns: 1194*7c478bd9Sstevel@tonic-gate ** true iff FallbackSmartHost should be tried. 1195*7c478bd9Sstevel@tonic-gate */ 1196*7c478bd9Sstevel@tonic-gate 1197*7c478bd9Sstevel@tonic-gate static bool 1198*7c478bd9Sstevel@tonic-gate should_try_fbsh(e, tried_fallbacksmarthost, hostbuf, hbsz, status) 1199*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 1200*7c478bd9Sstevel@tonic-gate bool *tried_fallbacksmarthost; 1201*7c478bd9Sstevel@tonic-gate char *hostbuf; 1202*7c478bd9Sstevel@tonic-gate size_t hbsz; 1203*7c478bd9Sstevel@tonic-gate int status; 1204*7c478bd9Sstevel@tonic-gate { 1205*7c478bd9Sstevel@tonic-gate /* 1206*7c478bd9Sstevel@tonic-gate ** If the host was not found and a FallbackSmartHost is defined 1207*7c478bd9Sstevel@tonic-gate ** (and we have not yet tried it), then make one last try with 1208*7c478bd9Sstevel@tonic-gate ** it as the host. 1209*7c478bd9Sstevel@tonic-gate */ 1210*7c478bd9Sstevel@tonic-gate 1211*7c478bd9Sstevel@tonic-gate if (status == EX_NOHOST && FallbackSmartHost != NULL && 1212*7c478bd9Sstevel@tonic-gate !*tried_fallbacksmarthost) 1213*7c478bd9Sstevel@tonic-gate { 1214*7c478bd9Sstevel@tonic-gate *tried_fallbacksmarthost = true; 1215*7c478bd9Sstevel@tonic-gate expand(FallbackSmartHost, hostbuf, hbsz, e); 1216*7c478bd9Sstevel@tonic-gate if (!wordinclass(hostbuf, 'w')) 1217*7c478bd9Sstevel@tonic-gate { 1218*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 1219*7c478bd9Sstevel@tonic-gate sm_dprintf("one last try with FallbackSmartHost %s\n", 1220*7c478bd9Sstevel@tonic-gate hostbuf); 1221*7c478bd9Sstevel@tonic-gate return true; 1222*7c478bd9Sstevel@tonic-gate } 1223*7c478bd9Sstevel@tonic-gate } 1224*7c478bd9Sstevel@tonic-gate return false; 1225*7c478bd9Sstevel@tonic-gate } 1226*7c478bd9Sstevel@tonic-gate /* 1227*7c478bd9Sstevel@tonic-gate ** DELIVER -- Deliver a message to a list of addresses. 1228*7c478bd9Sstevel@tonic-gate ** 1229*7c478bd9Sstevel@tonic-gate ** This routine delivers to everyone on the same host as the 1230*7c478bd9Sstevel@tonic-gate ** user on the head of the list. It is clever about mailers 1231*7c478bd9Sstevel@tonic-gate ** that don't handle multiple users. It is NOT guaranteed 1232*7c478bd9Sstevel@tonic-gate ** that it will deliver to all these addresses however -- so 1233*7c478bd9Sstevel@tonic-gate ** deliver should be called once for each address on the 1234*7c478bd9Sstevel@tonic-gate ** list. 1235*7c478bd9Sstevel@tonic-gate ** Deliver tries to be as opportunistic as possible about piggybacking 1236*7c478bd9Sstevel@tonic-gate ** messages. Some definitions to make understanding easier follow below. 1237*7c478bd9Sstevel@tonic-gate ** Piggybacking occurs when an existing connection to a mail host can 1238*7c478bd9Sstevel@tonic-gate ** be used to send the same message to more than one recipient at the 1239*7c478bd9Sstevel@tonic-gate ** same time. So "no piggybacking" means one message for one recipient 1240*7c478bd9Sstevel@tonic-gate ** per connection. "Intentional piggybacking" happens when the 1241*7c478bd9Sstevel@tonic-gate ** recipients' host address (not the mail host address) is used to 1242*7c478bd9Sstevel@tonic-gate ** attempt piggybacking. Recipients with the same host address 1243*7c478bd9Sstevel@tonic-gate ** have the same mail host. "Coincidental piggybacking" relies on 1244*7c478bd9Sstevel@tonic-gate ** piggybacking based on all the mail host addresses in the MX-RR. This 1245*7c478bd9Sstevel@tonic-gate ** is "coincidental" in the fact it could not be predicted until the 1246*7c478bd9Sstevel@tonic-gate ** MX Resource Records for the hosts were obtained and examined. For 1247*7c478bd9Sstevel@tonic-gate ** example (preference order and equivalence is important, not values): 1248*7c478bd9Sstevel@tonic-gate ** domain1 IN MX 10 mxhost-A 1249*7c478bd9Sstevel@tonic-gate ** IN MX 20 mxhost-B 1250*7c478bd9Sstevel@tonic-gate ** domain2 IN MX 4 mxhost-A 1251*7c478bd9Sstevel@tonic-gate ** IN MX 8 mxhost-B 1252*7c478bd9Sstevel@tonic-gate ** Domain1 and domain2 can piggyback the same message to mxhost-A or 1253*7c478bd9Sstevel@tonic-gate ** mxhost-B (if mxhost-A cannot be reached). 1254*7c478bd9Sstevel@tonic-gate ** "Coattail piggybacking" relaxes the strictness of "coincidental 1255*7c478bd9Sstevel@tonic-gate ** piggybacking" in the hope that most significant (lowest value) 1256*7c478bd9Sstevel@tonic-gate ** MX preference host(s) can create more piggybacking. For example 1257*7c478bd9Sstevel@tonic-gate ** (again, preference order and equivalence is important, not values): 1258*7c478bd9Sstevel@tonic-gate ** domain3 IN MX 100 mxhost-C 1259*7c478bd9Sstevel@tonic-gate ** IN MX 100 mxhost-D 1260*7c478bd9Sstevel@tonic-gate ** IN MX 200 mxhost-E 1261*7c478bd9Sstevel@tonic-gate ** domain4 IN MX 50 mxhost-C 1262*7c478bd9Sstevel@tonic-gate ** IN MX 50 mxhost-D 1263*7c478bd9Sstevel@tonic-gate ** IN MX 80 mxhost-F 1264*7c478bd9Sstevel@tonic-gate ** A message for domain3 and domain4 can piggyback to mxhost-C if mxhost-C 1265*7c478bd9Sstevel@tonic-gate ** is available. Same with mxhost-D because in both RR's the preference 1266*7c478bd9Sstevel@tonic-gate ** value is the same as mxhost-C, respectively. 1267*7c478bd9Sstevel@tonic-gate ** So deliver attempts coattail piggybacking when possible. If the 1268*7c478bd9Sstevel@tonic-gate ** first MX preference level hosts cannot be used then the piggybacking 1269*7c478bd9Sstevel@tonic-gate ** reverts to coincidental piggybacking. Using the above example you 1270*7c478bd9Sstevel@tonic-gate ** cannot deliver to mxhost-F for domain3 regardless of preference value. 1271*7c478bd9Sstevel@tonic-gate ** ("Coattail" from "riding on the coattails of your predecessor" meaning 1272*7c478bd9Sstevel@tonic-gate ** gaining benefit from a predecessor effort with no or little addition 1273*7c478bd9Sstevel@tonic-gate ** effort. The predecessor here being the preceding MX RR). 1274*7c478bd9Sstevel@tonic-gate ** 1275*7c478bd9Sstevel@tonic-gate ** Parameters: 1276*7c478bd9Sstevel@tonic-gate ** e -- the envelope to deliver. 1277*7c478bd9Sstevel@tonic-gate ** firstto -- head of the address list to deliver to. 1278*7c478bd9Sstevel@tonic-gate ** 1279*7c478bd9Sstevel@tonic-gate ** Returns: 1280*7c478bd9Sstevel@tonic-gate ** zero -- successfully delivered. 1281*7c478bd9Sstevel@tonic-gate ** else -- some failure, see ExitStat for more info. 1282*7c478bd9Sstevel@tonic-gate ** 1283*7c478bd9Sstevel@tonic-gate ** Side Effects: 1284*7c478bd9Sstevel@tonic-gate ** The standard input is passed off to someone. 1285*7c478bd9Sstevel@tonic-gate */ 1286*7c478bd9Sstevel@tonic-gate 1287*7c478bd9Sstevel@tonic-gate static int 1288*7c478bd9Sstevel@tonic-gate deliver(e, firstto) 1289*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 1290*7c478bd9Sstevel@tonic-gate ADDRESS *firstto; 1291*7c478bd9Sstevel@tonic-gate { 1292*7c478bd9Sstevel@tonic-gate char *host; /* host being sent to */ 1293*7c478bd9Sstevel@tonic-gate char *user; /* user being sent to */ 1294*7c478bd9Sstevel@tonic-gate char **pvp; 1295*7c478bd9Sstevel@tonic-gate register char **mvp; 1296*7c478bd9Sstevel@tonic-gate register char *p; 1297*7c478bd9Sstevel@tonic-gate register MAILER *m; /* mailer for this recipient */ 1298*7c478bd9Sstevel@tonic-gate ADDRESS *volatile ctladdr; 1299*7c478bd9Sstevel@tonic-gate #if HASSETUSERCONTEXT 1300*7c478bd9Sstevel@tonic-gate ADDRESS *volatile contextaddr = NULL; 1301*7c478bd9Sstevel@tonic-gate #endif /* HASSETUSERCONTEXT */ 1302*7c478bd9Sstevel@tonic-gate register MCI *volatile mci; 1303*7c478bd9Sstevel@tonic-gate register ADDRESS *SM_NONVOLATILE to = firstto; 1304*7c478bd9Sstevel@tonic-gate volatile bool clever = false; /* running user smtp to this mailer */ 1305*7c478bd9Sstevel@tonic-gate ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ 1306*7c478bd9Sstevel@tonic-gate int rcode; /* response code */ 1307*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE int lmtp_rcode = EX_OK; 1308*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE int nummxhosts = 0; /* number of MX hosts available */ 1309*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE int hostnum = 0; /* current MX host index */ 1310*7c478bd9Sstevel@tonic-gate char *firstsig; /* signature of firstto */ 1311*7c478bd9Sstevel@tonic-gate volatile pid_t pid = -1; 1312*7c478bd9Sstevel@tonic-gate char *volatile curhost; 1313*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE unsigned short port = 0; 1314*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE time_t enough = 0; 1315*7c478bd9Sstevel@tonic-gate #if NETUNIX 1316*7c478bd9Sstevel@tonic-gate char *SM_NONVOLATILE mux_path = NULL; /* path to UNIX domain socket */ 1317*7c478bd9Sstevel@tonic-gate #endif /* NETUNIX */ 1318*7c478bd9Sstevel@tonic-gate time_t xstart; 1319*7c478bd9Sstevel@tonic-gate bool suidwarn; 1320*7c478bd9Sstevel@tonic-gate bool anyok; /* at least one address was OK */ 1321*7c478bd9Sstevel@tonic-gate SM_NONVOLATILE bool goodmxfound = false; /* at least one MX was OK */ 1322*7c478bd9Sstevel@tonic-gate bool ovr; 1323*7c478bd9Sstevel@tonic-gate bool quarantine; 1324*7c478bd9Sstevel@tonic-gate int strsize; 1325*7c478bd9Sstevel@tonic-gate int rcptcount; 1326*7c478bd9Sstevel@tonic-gate int ret; 1327*7c478bd9Sstevel@tonic-gate static int tobufsize = 0; 1328*7c478bd9Sstevel@tonic-gate static char *tobuf = NULL; 1329*7c478bd9Sstevel@tonic-gate char *rpath; /* translated return path */ 1330*7c478bd9Sstevel@tonic-gate int mpvect[2]; 1331*7c478bd9Sstevel@tonic-gate int rpvect[2]; 1332*7c478bd9Sstevel@tonic-gate char *mxhosts[MAXMXHOSTS + 1]; 1333*7c478bd9Sstevel@tonic-gate char *pv[MAXPV + 1]; 1334*7c478bd9Sstevel@tonic-gate char buf[MAXNAME + 1]; 1335*7c478bd9Sstevel@tonic-gate char cbuf[MAXPATHLEN]; 1336*7c478bd9Sstevel@tonic-gate 1337*7c478bd9Sstevel@tonic-gate errno = 0; 1338*7c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 1339*7c478bd9Sstevel@tonic-gate return 0; 1340*7c478bd9Sstevel@tonic-gate 1341*7c478bd9Sstevel@tonic-gate suidwarn = geteuid() == 0; 1342*7c478bd9Sstevel@tonic-gate 1343*7c478bd9Sstevel@tonic-gate m = to->q_mailer; 1344*7c478bd9Sstevel@tonic-gate host = to->q_host; 1345*7c478bd9Sstevel@tonic-gate CurEnv = e; /* just in case */ 1346*7c478bd9Sstevel@tonic-gate e->e_statmsg = NULL; 1347*7c478bd9Sstevel@tonic-gate SmtpError[0] = '\0'; 1348*7c478bd9Sstevel@tonic-gate xstart = curtime(); 1349*7c478bd9Sstevel@tonic-gate 1350*7c478bd9Sstevel@tonic-gate if (tTd(10, 1)) 1351*7c478bd9Sstevel@tonic-gate sm_dprintf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", 1352*7c478bd9Sstevel@tonic-gate e->e_id, m->m_name, host, to->q_user); 1353*7c478bd9Sstevel@tonic-gate if (tTd(10, 100)) 1354*7c478bd9Sstevel@tonic-gate printopenfds(false); 1355*7c478bd9Sstevel@tonic-gate 1356*7c478bd9Sstevel@tonic-gate /* 1357*7c478bd9Sstevel@tonic-gate ** Clear {client_*} macros if this is a bounce message to 1358*7c478bd9Sstevel@tonic-gate ** prevent rejection by check_compat ruleset. 1359*7c478bd9Sstevel@tonic-gate */ 1360*7c478bd9Sstevel@tonic-gate 1361*7c478bd9Sstevel@tonic-gate if (bitset(EF_RESPONSE, e->e_flags)) 1362*7c478bd9Sstevel@tonic-gate { 1363*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_name}"), ""); 1364*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_ptr}"), ""); 1365*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_addr}"), ""); 1366*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_port}"), ""); 1367*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, macid("{client_resolve}"), ""); 1368*7c478bd9Sstevel@tonic-gate } 1369*7c478bd9Sstevel@tonic-gate 1370*7c478bd9Sstevel@tonic-gate SM_TRY 1371*7c478bd9Sstevel@tonic-gate { 1372*7c478bd9Sstevel@tonic-gate ADDRESS *skip_back = NULL; 1373*7c478bd9Sstevel@tonic-gate 1374*7c478bd9Sstevel@tonic-gate /* 1375*7c478bd9Sstevel@tonic-gate ** Do initial argv setup. 1376*7c478bd9Sstevel@tonic-gate ** Insert the mailer name. Notice that $x expansion is 1377*7c478bd9Sstevel@tonic-gate ** NOT done on the mailer name. Then, if the mailer has 1378*7c478bd9Sstevel@tonic-gate ** a picky -f flag, we insert it as appropriate. This 1379*7c478bd9Sstevel@tonic-gate ** code does not check for 'pv' overflow; this places a 1380*7c478bd9Sstevel@tonic-gate ** manifest lower limit of 4 for MAXPV. 1381*7c478bd9Sstevel@tonic-gate ** The from address rewrite is expected to make 1382*7c478bd9Sstevel@tonic-gate ** the address relative to the other end. 1383*7c478bd9Sstevel@tonic-gate */ 1384*7c478bd9Sstevel@tonic-gate 1385*7c478bd9Sstevel@tonic-gate /* rewrite from address, using rewriting rules */ 1386*7c478bd9Sstevel@tonic-gate rcode = EX_OK; 1387*7c478bd9Sstevel@tonic-gate if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 1388*7c478bd9Sstevel@tonic-gate p = e->e_sender; 1389*7c478bd9Sstevel@tonic-gate else 1390*7c478bd9Sstevel@tonic-gate p = e->e_from.q_paddr; 1391*7c478bd9Sstevel@tonic-gate rpath = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); 1392*7c478bd9Sstevel@tonic-gate if (strlen(rpath) > MAXSHORTSTR) 1393*7c478bd9Sstevel@tonic-gate { 1394*7c478bd9Sstevel@tonic-gate rpath = shortenstring(rpath, MAXSHORTSTR); 1395*7c478bd9Sstevel@tonic-gate 1396*7c478bd9Sstevel@tonic-gate /* avoid bogus errno */ 1397*7c478bd9Sstevel@tonic-gate errno = 0; 1398*7c478bd9Sstevel@tonic-gate syserr("remotename: huge return path %s", rpath); 1399*7c478bd9Sstevel@tonic-gate } 1400*7c478bd9Sstevel@tonic-gate rpath = sm_rpool_strdup_x(e->e_rpool, rpath); 1401*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'g', rpath); 1402*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'h', host); 1403*7c478bd9Sstevel@tonic-gate Errors = 0; 1404*7c478bd9Sstevel@tonic-gate pvp = pv; 1405*7c478bd9Sstevel@tonic-gate *pvp++ = m->m_argv[0]; 1406*7c478bd9Sstevel@tonic-gate 1407*7c478bd9Sstevel@tonic-gate /* ignore long term host status information if mailer flag W is set */ 1408*7c478bd9Sstevel@tonic-gate if (bitnset(M_NOHOSTSTAT, m->m_flags)) 1409*7c478bd9Sstevel@tonic-gate IgnoreHostStatus = true; 1410*7c478bd9Sstevel@tonic-gate 1411*7c478bd9Sstevel@tonic-gate /* insert -f or -r flag as appropriate */ 1412*7c478bd9Sstevel@tonic-gate if (FromFlag && 1413*7c478bd9Sstevel@tonic-gate (bitnset(M_FOPT, m->m_flags) || 1414*7c478bd9Sstevel@tonic-gate bitnset(M_ROPT, m->m_flags))) 1415*7c478bd9Sstevel@tonic-gate { 1416*7c478bd9Sstevel@tonic-gate if (bitnset(M_FOPT, m->m_flags)) 1417*7c478bd9Sstevel@tonic-gate *pvp++ = "-f"; 1418*7c478bd9Sstevel@tonic-gate else 1419*7c478bd9Sstevel@tonic-gate *pvp++ = "-r"; 1420*7c478bd9Sstevel@tonic-gate *pvp++ = rpath; 1421*7c478bd9Sstevel@tonic-gate } 1422*7c478bd9Sstevel@tonic-gate 1423*7c478bd9Sstevel@tonic-gate /* 1424*7c478bd9Sstevel@tonic-gate ** Append the other fixed parts of the argv. These run 1425*7c478bd9Sstevel@tonic-gate ** up to the first entry containing "$u". There can only 1426*7c478bd9Sstevel@tonic-gate ** be one of these, and there are only a few more slots 1427*7c478bd9Sstevel@tonic-gate ** in the pv after it. 1428*7c478bd9Sstevel@tonic-gate */ 1429*7c478bd9Sstevel@tonic-gate 1430*7c478bd9Sstevel@tonic-gate for (mvp = m->m_argv; (p = *++mvp) != NULL; ) 1431*7c478bd9Sstevel@tonic-gate { 1432*7c478bd9Sstevel@tonic-gate /* can't use strchr here because of sign extension problems */ 1433*7c478bd9Sstevel@tonic-gate while (*p != '\0') 1434*7c478bd9Sstevel@tonic-gate { 1435*7c478bd9Sstevel@tonic-gate if ((*p++ & 0377) == MACROEXPAND) 1436*7c478bd9Sstevel@tonic-gate { 1437*7c478bd9Sstevel@tonic-gate if (*p == 'u') 1438*7c478bd9Sstevel@tonic-gate break; 1439*7c478bd9Sstevel@tonic-gate } 1440*7c478bd9Sstevel@tonic-gate } 1441*7c478bd9Sstevel@tonic-gate 1442*7c478bd9Sstevel@tonic-gate if (*p != '\0') 1443*7c478bd9Sstevel@tonic-gate break; 1444*7c478bd9Sstevel@tonic-gate 1445*7c478bd9Sstevel@tonic-gate /* this entry is safe -- go ahead and process it */ 1446*7c478bd9Sstevel@tonic-gate expand(*mvp, buf, sizeof buf, e); 1447*7c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 1448*7c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV - 3]) 1449*7c478bd9Sstevel@tonic-gate { 1450*7c478bd9Sstevel@tonic-gate syserr("554 5.3.5 Too many parameters to %s before $u", 1451*7c478bd9Sstevel@tonic-gate pv[0]); 1452*7c478bd9Sstevel@tonic-gate rcode = -1; 1453*7c478bd9Sstevel@tonic-gate goto cleanup; 1454*7c478bd9Sstevel@tonic-gate } 1455*7c478bd9Sstevel@tonic-gate } 1456*7c478bd9Sstevel@tonic-gate 1457*7c478bd9Sstevel@tonic-gate /* 1458*7c478bd9Sstevel@tonic-gate ** If we have no substitution for the user name in the argument 1459*7c478bd9Sstevel@tonic-gate ** list, we know that we must supply the names otherwise -- and 1460*7c478bd9Sstevel@tonic-gate ** SMTP is the answer!! 1461*7c478bd9Sstevel@tonic-gate */ 1462*7c478bd9Sstevel@tonic-gate 1463*7c478bd9Sstevel@tonic-gate if (*mvp == NULL) 1464*7c478bd9Sstevel@tonic-gate { 1465*7c478bd9Sstevel@tonic-gate /* running LMTP or SMTP */ 1466*7c478bd9Sstevel@tonic-gate clever = true; 1467*7c478bd9Sstevel@tonic-gate *pvp = NULL; 1468*7c478bd9Sstevel@tonic-gate } 1469*7c478bd9Sstevel@tonic-gate else if (bitnset(M_LMTP, m->m_flags)) 1470*7c478bd9Sstevel@tonic-gate { 1471*7c478bd9Sstevel@tonic-gate /* not running LMTP */ 1472*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NULL, 1473*7c478bd9Sstevel@tonic-gate "Warning: mailer %s: LMTP flag (F=z) turned off", 1474*7c478bd9Sstevel@tonic-gate m->m_name); 1475*7c478bd9Sstevel@tonic-gate clrbitn(M_LMTP, m->m_flags); 1476*7c478bd9Sstevel@tonic-gate } 1477*7c478bd9Sstevel@tonic-gate 1478*7c478bd9Sstevel@tonic-gate /* 1479*7c478bd9Sstevel@tonic-gate ** At this point *mvp points to the argument with $u. We 1480*7c478bd9Sstevel@tonic-gate ** run through our address list and append all the addresses 1481*7c478bd9Sstevel@tonic-gate ** we can. If we run out of space, do not fret! We can 1482*7c478bd9Sstevel@tonic-gate ** always send another copy later. 1483*7c478bd9Sstevel@tonic-gate */ 1484*7c478bd9Sstevel@tonic-gate 1485*7c478bd9Sstevel@tonic-gate e->e_to = NULL; 1486*7c478bd9Sstevel@tonic-gate strsize = 2; 1487*7c478bd9Sstevel@tonic-gate rcptcount = 0; 1488*7c478bd9Sstevel@tonic-gate ctladdr = NULL; 1489*7c478bd9Sstevel@tonic-gate if (firstto->q_signature == NULL) 1490*7c478bd9Sstevel@tonic-gate firstto->q_signature = hostsignature(firstto->q_mailer, 1491*7c478bd9Sstevel@tonic-gate firstto->q_host); 1492*7c478bd9Sstevel@tonic-gate firstsig = firstto->q_signature; 1493*7c478bd9Sstevel@tonic-gate 1494*7c478bd9Sstevel@tonic-gate for (; to != NULL; to = to->q_next) 1495*7c478bd9Sstevel@tonic-gate { 1496*7c478bd9Sstevel@tonic-gate /* avoid sending multiple recipients to dumb mailers */ 1497*7c478bd9Sstevel@tonic-gate if (tochain != NULL && !bitnset(M_MUSER, m->m_flags)) 1498*7c478bd9Sstevel@tonic-gate break; 1499*7c478bd9Sstevel@tonic-gate 1500*7c478bd9Sstevel@tonic-gate /* if already sent or not for this host, don't send */ 1501*7c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) /* already sent; look at next */ 1502*7c478bd9Sstevel@tonic-gate continue; 1503*7c478bd9Sstevel@tonic-gate 1504*7c478bd9Sstevel@tonic-gate /* 1505*7c478bd9Sstevel@tonic-gate ** Must be same mailer to keep grouping rcpts. 1506*7c478bd9Sstevel@tonic-gate ** If mailers don't match: continue; sendqueue is not 1507*7c478bd9Sstevel@tonic-gate ** sorted by mailers, so don't break; 1508*7c478bd9Sstevel@tonic-gate */ 1509*7c478bd9Sstevel@tonic-gate 1510*7c478bd9Sstevel@tonic-gate if (to->q_mailer != firstto->q_mailer) 1511*7c478bd9Sstevel@tonic-gate continue; 1512*7c478bd9Sstevel@tonic-gate 1513*7c478bd9Sstevel@tonic-gate if (to->q_signature == NULL) /* for safety */ 1514*7c478bd9Sstevel@tonic-gate to->q_signature = hostsignature(to->q_mailer, 1515*7c478bd9Sstevel@tonic-gate to->q_host); 1516*7c478bd9Sstevel@tonic-gate 1517*7c478bd9Sstevel@tonic-gate /* 1518*7c478bd9Sstevel@tonic-gate ** This is for coincidental and tailcoat piggybacking messages 1519*7c478bd9Sstevel@tonic-gate ** to the same mail host. While the signatures are identical 1520*7c478bd9Sstevel@tonic-gate ** (that's the MX-RR's are identical) we can do coincidental 1521*7c478bd9Sstevel@tonic-gate ** piggybacking. We try hard for coattail piggybacking 1522*7c478bd9Sstevel@tonic-gate ** with the same mail host when the next recipient has the 1523*7c478bd9Sstevel@tonic-gate ** same host at lowest preference. It may be that this 1524*7c478bd9Sstevel@tonic-gate ** won't work out, so 'skip_back' is maintained if a backup 1525*7c478bd9Sstevel@tonic-gate ** to coincidental piggybacking or full signature must happen. 1526*7c478bd9Sstevel@tonic-gate */ 1527*7c478bd9Sstevel@tonic-gate 1528*7c478bd9Sstevel@tonic-gate ret = firstto == to ? HS_MATCH_FULL : 1529*7c478bd9Sstevel@tonic-gate coloncmp(to->q_signature, firstsig); 1530*7c478bd9Sstevel@tonic-gate if (ret == HS_MATCH_FULL) 1531*7c478bd9Sstevel@tonic-gate skip_back = to; 1532*7c478bd9Sstevel@tonic-gate else if (ret == HS_MATCH_NO) 1533*7c478bd9Sstevel@tonic-gate break; 1534*7c478bd9Sstevel@tonic-gate 1535*7c478bd9Sstevel@tonic-gate if (!clever) 1536*7c478bd9Sstevel@tonic-gate { 1537*7c478bd9Sstevel@tonic-gate /* avoid overflowing tobuf */ 1538*7c478bd9Sstevel@tonic-gate strsize += strlen(to->q_paddr) + 1; 1539*7c478bd9Sstevel@tonic-gate if (strsize > TOBUFSIZE) 1540*7c478bd9Sstevel@tonic-gate break; 1541*7c478bd9Sstevel@tonic-gate } 1542*7c478bd9Sstevel@tonic-gate 1543*7c478bd9Sstevel@tonic-gate if (++rcptcount > to->q_mailer->m_maxrcpt) 1544*7c478bd9Sstevel@tonic-gate break; 1545*7c478bd9Sstevel@tonic-gate 1546*7c478bd9Sstevel@tonic-gate if (tTd(10, 1)) 1547*7c478bd9Sstevel@tonic-gate { 1548*7c478bd9Sstevel@tonic-gate sm_dprintf("\nsend to "); 1549*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), to, false); 1550*7c478bd9Sstevel@tonic-gate } 1551*7c478bd9Sstevel@tonic-gate 1552*7c478bd9Sstevel@tonic-gate /* compute effective uid/gid when sending */ 1553*7c478bd9Sstevel@tonic-gate if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) 1554*7c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT 1555*7c478bd9Sstevel@tonic-gate contextaddr = ctladdr = getctladdr(to); 1556*7c478bd9Sstevel@tonic-gate # else /* HASSETUSERCONTEXT */ 1557*7c478bd9Sstevel@tonic-gate ctladdr = getctladdr(to); 1558*7c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */ 1559*7c478bd9Sstevel@tonic-gate 1560*7c478bd9Sstevel@tonic-gate if (tTd(10, 2)) 1561*7c478bd9Sstevel@tonic-gate { 1562*7c478bd9Sstevel@tonic-gate sm_dprintf("ctladdr="); 1563*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ctladdr, false); 1564*7c478bd9Sstevel@tonic-gate } 1565*7c478bd9Sstevel@tonic-gate 1566*7c478bd9Sstevel@tonic-gate user = to->q_user; 1567*7c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 1568*7c478bd9Sstevel@tonic-gate 1569*7c478bd9Sstevel@tonic-gate /* 1570*7c478bd9Sstevel@tonic-gate ** Check to see that these people are allowed to 1571*7c478bd9Sstevel@tonic-gate ** talk to each other. 1572*7c478bd9Sstevel@tonic-gate ** Check also for overflow of e_msgsize. 1573*7c478bd9Sstevel@tonic-gate */ 1574*7c478bd9Sstevel@tonic-gate 1575*7c478bd9Sstevel@tonic-gate if (m->m_maxsize != 0 && 1576*7c478bd9Sstevel@tonic-gate (e->e_msgsize > m->m_maxsize || e->e_msgsize < 0)) 1577*7c478bd9Sstevel@tonic-gate { 1578*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_NO_BODY_RETN; 1579*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, to->q_mailer->m_flags)) 1580*7c478bd9Sstevel@tonic-gate to->q_status = "5.2.3"; 1581*7c478bd9Sstevel@tonic-gate else 1582*7c478bd9Sstevel@tonic-gate to->q_status = "5.3.4"; 1583*7c478bd9Sstevel@tonic-gate 1584*7c478bd9Sstevel@tonic-gate /* set to->q_rstatus = NULL; or to the following? */ 1585*7c478bd9Sstevel@tonic-gate usrerrenh(to->q_status, 1586*7c478bd9Sstevel@tonic-gate "552 Message is too large; %ld bytes max", 1587*7c478bd9Sstevel@tonic-gate m->m_maxsize); 1588*7c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, EX_UNAVAILABLE, false); 1589*7c478bd9Sstevel@tonic-gate giveresponse(EX_UNAVAILABLE, to->q_status, m, 1590*7c478bd9Sstevel@tonic-gate NULL, ctladdr, xstart, e, to); 1591*7c478bd9Sstevel@tonic-gate continue; 1592*7c478bd9Sstevel@tonic-gate } 1593*7c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 1594*7c478bd9Sstevel@tonic-gate ovr = true; 1595*7c478bd9Sstevel@tonic-gate 1596*7c478bd9Sstevel@tonic-gate /* do config file checking of compatibility */ 1597*7c478bd9Sstevel@tonic-gate quarantine = (e->e_quarmsg != NULL); 1598*7c478bd9Sstevel@tonic-gate rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, 1599*7c478bd9Sstevel@tonic-gate e, RSF_RMCOMM|RSF_COUNT, 3, NULL, 1600*7c478bd9Sstevel@tonic-gate e->e_id); 1601*7c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 1602*7c478bd9Sstevel@tonic-gate { 1603*7c478bd9Sstevel@tonic-gate /* do in-code checking if not discarding */ 1604*7c478bd9Sstevel@tonic-gate if (!bitset(EF_DISCARD, e->e_flags)) 1605*7c478bd9Sstevel@tonic-gate { 1606*7c478bd9Sstevel@tonic-gate rcode = checkcompat(to, e); 1607*7c478bd9Sstevel@tonic-gate ovr = false; 1608*7c478bd9Sstevel@tonic-gate } 1609*7c478bd9Sstevel@tonic-gate } 1610*7c478bd9Sstevel@tonic-gate if (rcode != EX_OK) 1611*7c478bd9Sstevel@tonic-gate { 1612*7c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, rcode, ovr); 1613*7c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, 1614*7c478bd9Sstevel@tonic-gate NULL, ctladdr, xstart, e, to); 1615*7c478bd9Sstevel@tonic-gate continue; 1616*7c478bd9Sstevel@tonic-gate } 1617*7c478bd9Sstevel@tonic-gate if (!quarantine && e->e_quarmsg != NULL) 1618*7c478bd9Sstevel@tonic-gate { 1619*7c478bd9Sstevel@tonic-gate /* 1620*7c478bd9Sstevel@tonic-gate ** check_compat or checkcompat() has tried 1621*7c478bd9Sstevel@tonic-gate ** to quarantine but that isn't supported. 1622*7c478bd9Sstevel@tonic-gate ** Revert the attempt. 1623*7c478bd9Sstevel@tonic-gate */ 1624*7c478bd9Sstevel@tonic-gate 1625*7c478bd9Sstevel@tonic-gate e->e_quarmsg = NULL; 1626*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 1627*7c478bd9Sstevel@tonic-gate macid("{quarantine}"), ""); 1628*7c478bd9Sstevel@tonic-gate } 1629*7c478bd9Sstevel@tonic-gate if (bitset(EF_DISCARD, e->e_flags)) 1630*7c478bd9Sstevel@tonic-gate { 1631*7c478bd9Sstevel@tonic-gate if (tTd(10, 5)) 1632*7c478bd9Sstevel@tonic-gate { 1633*7c478bd9Sstevel@tonic-gate sm_dprintf("deliver: discarding recipient "); 1634*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), to, false); 1635*7c478bd9Sstevel@tonic-gate } 1636*7c478bd9Sstevel@tonic-gate 1637*7c478bd9Sstevel@tonic-gate /* pretend the message was sent */ 1638*7c478bd9Sstevel@tonic-gate /* XXX should we log something here? */ 1639*7c478bd9Sstevel@tonic-gate to->q_state = QS_DISCARDED; 1640*7c478bd9Sstevel@tonic-gate 1641*7c478bd9Sstevel@tonic-gate /* 1642*7c478bd9Sstevel@tonic-gate ** Remove discard bit to prevent discard of 1643*7c478bd9Sstevel@tonic-gate ** future recipients. This is safe because the 1644*7c478bd9Sstevel@tonic-gate ** true "global discard" has been handled before 1645*7c478bd9Sstevel@tonic-gate ** we get here. 1646*7c478bd9Sstevel@tonic-gate */ 1647*7c478bd9Sstevel@tonic-gate 1648*7c478bd9Sstevel@tonic-gate e->e_flags &= ~EF_DISCARD; 1649*7c478bd9Sstevel@tonic-gate continue; 1650*7c478bd9Sstevel@tonic-gate } 1651*7c478bd9Sstevel@tonic-gate 1652*7c478bd9Sstevel@tonic-gate /* 1653*7c478bd9Sstevel@tonic-gate ** Strip quote bits from names if the mailer is dumb 1654*7c478bd9Sstevel@tonic-gate ** about them. 1655*7c478bd9Sstevel@tonic-gate */ 1656*7c478bd9Sstevel@tonic-gate 1657*7c478bd9Sstevel@tonic-gate if (bitnset(M_STRIPQ, m->m_flags)) 1658*7c478bd9Sstevel@tonic-gate { 1659*7c478bd9Sstevel@tonic-gate stripquotes(user); 1660*7c478bd9Sstevel@tonic-gate stripquotes(host); 1661*7c478bd9Sstevel@tonic-gate } 1662*7c478bd9Sstevel@tonic-gate 1663*7c478bd9Sstevel@tonic-gate /* 1664*7c478bd9Sstevel@tonic-gate ** Strip all leading backslashes if requested and the 1665*7c478bd9Sstevel@tonic-gate ** next character is alphanumerical (the latter can 1666*7c478bd9Sstevel@tonic-gate ** probably relaxed a bit, see RFC2821). 1667*7c478bd9Sstevel@tonic-gate */ 1668*7c478bd9Sstevel@tonic-gate 1669*7c478bd9Sstevel@tonic-gate if (bitnset(M_STRIPBACKSL, m->m_flags) && user[0] == '\\') 1670*7c478bd9Sstevel@tonic-gate stripbackslash(user); 1671*7c478bd9Sstevel@tonic-gate 1672*7c478bd9Sstevel@tonic-gate /* hack attack -- delivermail compatibility */ 1673*7c478bd9Sstevel@tonic-gate if (m == ProgMailer && *user == '|') 1674*7c478bd9Sstevel@tonic-gate user++; 1675*7c478bd9Sstevel@tonic-gate 1676*7c478bd9Sstevel@tonic-gate /* 1677*7c478bd9Sstevel@tonic-gate ** If an error message has already been given, don't 1678*7c478bd9Sstevel@tonic-gate ** bother to send to this address. 1679*7c478bd9Sstevel@tonic-gate ** 1680*7c478bd9Sstevel@tonic-gate ** >>>>>>>>>> This clause assumes that the local mailer 1681*7c478bd9Sstevel@tonic-gate ** >> NOTE >> cannot do any further aliasing; that 1682*7c478bd9Sstevel@tonic-gate ** >>>>>>>>>> function is subsumed by sendmail. 1683*7c478bd9Sstevel@tonic-gate */ 1684*7c478bd9Sstevel@tonic-gate 1685*7c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 1686*7c478bd9Sstevel@tonic-gate continue; 1687*7c478bd9Sstevel@tonic-gate 1688*7c478bd9Sstevel@tonic-gate /* 1689*7c478bd9Sstevel@tonic-gate ** See if this user name is "special". 1690*7c478bd9Sstevel@tonic-gate ** If the user name has a slash in it, assume that this 1691*7c478bd9Sstevel@tonic-gate ** is a file -- send it off without further ado. Note 1692*7c478bd9Sstevel@tonic-gate ** that this type of addresses is not processed along 1693*7c478bd9Sstevel@tonic-gate ** with the others, so we fudge on the To person. 1694*7c478bd9Sstevel@tonic-gate */ 1695*7c478bd9Sstevel@tonic-gate 1696*7c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[FILE]") == 0) 1697*7c478bd9Sstevel@tonic-gate { 1698*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'u', user); 1699*7c478bd9Sstevel@tonic-gate p = to->q_home; 1700*7c478bd9Sstevel@tonic-gate if (p == NULL && ctladdr != NULL) 1701*7c478bd9Sstevel@tonic-gate p = ctladdr->q_home; 1702*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'z', p); 1703*7c478bd9Sstevel@tonic-gate expand(m->m_argv[1], buf, sizeof buf, e); 1704*7c478bd9Sstevel@tonic-gate if (strlen(buf) > 0) 1705*7c478bd9Sstevel@tonic-gate rcode = mailfile(buf, m, ctladdr, SFF_CREAT, e); 1706*7c478bd9Sstevel@tonic-gate else 1707*7c478bd9Sstevel@tonic-gate { 1708*7c478bd9Sstevel@tonic-gate syserr("empty filename specification for mailer %s", 1709*7c478bd9Sstevel@tonic-gate m->m_name); 1710*7c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 1711*7c478bd9Sstevel@tonic-gate } 1712*7c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, NULL, 1713*7c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 1714*7c478bd9Sstevel@tonic-gate markfailure(e, to, NULL, rcode, true); 1715*7c478bd9Sstevel@tonic-gate e->e_nsent++; 1716*7c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 1717*7c478bd9Sstevel@tonic-gate { 1718*7c478bd9Sstevel@tonic-gate to->q_state = QS_SENT; 1719*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 1720*7c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags)) 1721*7c478bd9Sstevel@tonic-gate { 1722*7c478bd9Sstevel@tonic-gate to->q_flags |= QDELIVERED; 1723*7c478bd9Sstevel@tonic-gate to->q_status = "2.1.5"; 1724*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, 1725*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 1726*7c478bd9Sstevel@tonic-gate "%s... Successfully delivered\n", 1727*7c478bd9Sstevel@tonic-gate to->q_paddr); 1728*7c478bd9Sstevel@tonic-gate } 1729*7c478bd9Sstevel@tonic-gate } 1730*7c478bd9Sstevel@tonic-gate to->q_statdate = curtime(); 1731*7c478bd9Sstevel@tonic-gate markstats(e, to, STATS_NORMAL); 1732*7c478bd9Sstevel@tonic-gate continue; 1733*7c478bd9Sstevel@tonic-gate } 1734*7c478bd9Sstevel@tonic-gate 1735*7c478bd9Sstevel@tonic-gate /* 1736*7c478bd9Sstevel@tonic-gate ** Address is verified -- add this user to mailer 1737*7c478bd9Sstevel@tonic-gate ** argv, and add it to the print list of recipients. 1738*7c478bd9Sstevel@tonic-gate */ 1739*7c478bd9Sstevel@tonic-gate 1740*7c478bd9Sstevel@tonic-gate /* link together the chain of recipients */ 1741*7c478bd9Sstevel@tonic-gate to->q_tchain = tochain; 1742*7c478bd9Sstevel@tonic-gate tochain = to; 1743*7c478bd9Sstevel@tonic-gate e->e_to = "[CHAIN]"; 1744*7c478bd9Sstevel@tonic-gate 1745*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'u', user); /* to user */ 1746*7c478bd9Sstevel@tonic-gate p = to->q_home; 1747*7c478bd9Sstevel@tonic-gate if (p == NULL && ctladdr != NULL) 1748*7c478bd9Sstevel@tonic-gate p = ctladdr->q_home; 1749*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'z', p); /* user's home */ 1750*7c478bd9Sstevel@tonic-gate 1751*7c478bd9Sstevel@tonic-gate /* set the ${dsn_notify} macro if applicable */ 1752*7c478bd9Sstevel@tonic-gate if (bitset(QHASNOTIFY, to->q_flags)) 1753*7c478bd9Sstevel@tonic-gate { 1754*7c478bd9Sstevel@tonic-gate char notify[MAXLINE]; 1755*7c478bd9Sstevel@tonic-gate 1756*7c478bd9Sstevel@tonic-gate notify[0] = '\0'; 1757*7c478bd9Sstevel@tonic-gate if (bitset(QPINGONSUCCESS, to->q_flags)) 1758*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "SUCCESS,", 1759*7c478bd9Sstevel@tonic-gate sizeof notify); 1760*7c478bd9Sstevel@tonic-gate if (bitset(QPINGONFAILURE, to->q_flags)) 1761*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "FAILURE,", 1762*7c478bd9Sstevel@tonic-gate sizeof notify); 1763*7c478bd9Sstevel@tonic-gate if (bitset(QPINGONDELAY, to->q_flags)) 1764*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "DELAY,", 1765*7c478bd9Sstevel@tonic-gate sizeof notify); 1766*7c478bd9Sstevel@tonic-gate 1767*7c478bd9Sstevel@tonic-gate /* Set to NEVER or drop trailing comma */ 1768*7c478bd9Sstevel@tonic-gate if (notify[0] == '\0') 1769*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(notify, "NEVER", 1770*7c478bd9Sstevel@tonic-gate sizeof notify); 1771*7c478bd9Sstevel@tonic-gate else 1772*7c478bd9Sstevel@tonic-gate notify[strlen(notify) - 1] = '\0'; 1773*7c478bd9Sstevel@tonic-gate 1774*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_TEMP, 1775*7c478bd9Sstevel@tonic-gate macid("{dsn_notify}"), notify); 1776*7c478bd9Sstevel@tonic-gate } 1777*7c478bd9Sstevel@tonic-gate else 1778*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 1779*7c478bd9Sstevel@tonic-gate macid("{dsn_notify}"), NULL); 1780*7c478bd9Sstevel@tonic-gate 1781*7c478bd9Sstevel@tonic-gate /* 1782*7c478bd9Sstevel@tonic-gate ** Expand out this user into argument list. 1783*7c478bd9Sstevel@tonic-gate */ 1784*7c478bd9Sstevel@tonic-gate 1785*7c478bd9Sstevel@tonic-gate if (!clever) 1786*7c478bd9Sstevel@tonic-gate { 1787*7c478bd9Sstevel@tonic-gate expand(*mvp, buf, sizeof buf, e); 1788*7c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 1789*7c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV - 2]) 1790*7c478bd9Sstevel@tonic-gate { 1791*7c478bd9Sstevel@tonic-gate /* allow some space for trailing parms */ 1792*7c478bd9Sstevel@tonic-gate break; 1793*7c478bd9Sstevel@tonic-gate } 1794*7c478bd9Sstevel@tonic-gate } 1795*7c478bd9Sstevel@tonic-gate } 1796*7c478bd9Sstevel@tonic-gate 1797*7c478bd9Sstevel@tonic-gate /* see if any addresses still exist */ 1798*7c478bd9Sstevel@tonic-gate if (tochain == NULL) 1799*7c478bd9Sstevel@tonic-gate { 1800*7c478bd9Sstevel@tonic-gate rcode = 0; 1801*7c478bd9Sstevel@tonic-gate goto cleanup; 1802*7c478bd9Sstevel@tonic-gate } 1803*7c478bd9Sstevel@tonic-gate 1804*7c478bd9Sstevel@tonic-gate /* print out messages as full list */ 1805*7c478bd9Sstevel@tonic-gate strsize = 1; 1806*7c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 1807*7c478bd9Sstevel@tonic-gate strsize += strlen(to->q_paddr) + 1; 1808*7c478bd9Sstevel@tonic-gate if (strsize < TOBUFSIZE) 1809*7c478bd9Sstevel@tonic-gate strsize = TOBUFSIZE; 1810*7c478bd9Sstevel@tonic-gate if (strsize > tobufsize) 1811*7c478bd9Sstevel@tonic-gate { 1812*7c478bd9Sstevel@tonic-gate SM_FREE_CLR(tobuf); 1813*7c478bd9Sstevel@tonic-gate tobuf = sm_pmalloc_x(strsize); 1814*7c478bd9Sstevel@tonic-gate tobufsize = strsize; 1815*7c478bd9Sstevel@tonic-gate } 1816*7c478bd9Sstevel@tonic-gate p = tobuf; 1817*7c478bd9Sstevel@tonic-gate *p = '\0'; 1818*7c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 1819*7c478bd9Sstevel@tonic-gate { 1820*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(p, tobufsize - (p - tobuf), 2, 1821*7c478bd9Sstevel@tonic-gate ",", to->q_paddr); 1822*7c478bd9Sstevel@tonic-gate p += strlen(p); 1823*7c478bd9Sstevel@tonic-gate } 1824*7c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 1825*7c478bd9Sstevel@tonic-gate 1826*7c478bd9Sstevel@tonic-gate /* 1827*7c478bd9Sstevel@tonic-gate ** Fill out any parameters after the $u parameter. 1828*7c478bd9Sstevel@tonic-gate */ 1829*7c478bd9Sstevel@tonic-gate 1830*7c478bd9Sstevel@tonic-gate if (!clever) 1831*7c478bd9Sstevel@tonic-gate { 1832*7c478bd9Sstevel@tonic-gate while (*++mvp != NULL) 1833*7c478bd9Sstevel@tonic-gate { 1834*7c478bd9Sstevel@tonic-gate expand(*mvp, buf, sizeof buf, e); 1835*7c478bd9Sstevel@tonic-gate *pvp++ = sm_rpool_strdup_x(e->e_rpool, buf); 1836*7c478bd9Sstevel@tonic-gate if (pvp >= &pv[MAXPV]) 1837*7c478bd9Sstevel@tonic-gate syserr("554 5.3.0 deliver: pv overflow after $u for %s", 1838*7c478bd9Sstevel@tonic-gate pv[0]); 1839*7c478bd9Sstevel@tonic-gate } 1840*7c478bd9Sstevel@tonic-gate } 1841*7c478bd9Sstevel@tonic-gate *pvp++ = NULL; 1842*7c478bd9Sstevel@tonic-gate 1843*7c478bd9Sstevel@tonic-gate /* 1844*7c478bd9Sstevel@tonic-gate ** Call the mailer. 1845*7c478bd9Sstevel@tonic-gate ** The argument vector gets built, pipes 1846*7c478bd9Sstevel@tonic-gate ** are created as necessary, and we fork & exec as 1847*7c478bd9Sstevel@tonic-gate ** appropriate. 1848*7c478bd9Sstevel@tonic-gate ** If we are running SMTP, we just need to clean up. 1849*7c478bd9Sstevel@tonic-gate */ 1850*7c478bd9Sstevel@tonic-gate 1851*7c478bd9Sstevel@tonic-gate /* XXX this seems a bit wierd */ 1852*7c478bd9Sstevel@tonic-gate if (ctladdr == NULL && m != ProgMailer && m != FileMailer && 1853*7c478bd9Sstevel@tonic-gate bitset(QGOODUID, e->e_from.q_flags)) 1854*7c478bd9Sstevel@tonic-gate ctladdr = &e->e_from; 1855*7c478bd9Sstevel@tonic-gate 1856*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 1857*7c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 1858*7c478bd9Sstevel@tonic-gate _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 1859*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 1860*7c478bd9Sstevel@tonic-gate 1861*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 1862*7c478bd9Sstevel@tonic-gate { 1863*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer:"); 1864*7c478bd9Sstevel@tonic-gate printav(sm_debug_file(), pv); 1865*7c478bd9Sstevel@tonic-gate } 1866*7c478bd9Sstevel@tonic-gate errno = 0; 1867*7c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 1868*7c478bd9Sstevel@tonic-gate CurHostName = NULL; 1869*7c478bd9Sstevel@tonic-gate 1870*7c478bd9Sstevel@tonic-gate /* 1871*7c478bd9Sstevel@tonic-gate ** Deal with the special case of mail handled through an IPC 1872*7c478bd9Sstevel@tonic-gate ** connection. 1873*7c478bd9Sstevel@tonic-gate ** In this case we don't actually fork. We must be 1874*7c478bd9Sstevel@tonic-gate ** running SMTP for this to work. We will return a 1875*7c478bd9Sstevel@tonic-gate ** zero pid to indicate that we are running IPC. 1876*7c478bd9Sstevel@tonic-gate ** We also handle a debug version that just talks to stdin/out. 1877*7c478bd9Sstevel@tonic-gate */ 1878*7c478bd9Sstevel@tonic-gate 1879*7c478bd9Sstevel@tonic-gate curhost = NULL; 1880*7c478bd9Sstevel@tonic-gate SmtpPhase = NULL; 1881*7c478bd9Sstevel@tonic-gate mci = NULL; 1882*7c478bd9Sstevel@tonic-gate 1883*7c478bd9Sstevel@tonic-gate #if XDEBUG 1884*7c478bd9Sstevel@tonic-gate { 1885*7c478bd9Sstevel@tonic-gate char wbuf[MAXLINE]; 1886*7c478bd9Sstevel@tonic-gate 1887*7c478bd9Sstevel@tonic-gate /* make absolutely certain 0, 1, and 2 are in use */ 1888*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", 1889*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 1890*7c478bd9Sstevel@tonic-gate m->m_name); 1891*7c478bd9Sstevel@tonic-gate checkfd012(wbuf); 1892*7c478bd9Sstevel@tonic-gate } 1893*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 1894*7c478bd9Sstevel@tonic-gate 1895*7c478bd9Sstevel@tonic-gate /* check for 8-bit available */ 1896*7c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 1897*7c478bd9Sstevel@tonic-gate bitnset(M_7BITS, m->m_flags) && 1898*7c478bd9Sstevel@tonic-gate (bitset(EF_DONT_MIME, e->e_flags) || 1899*7c478bd9Sstevel@tonic-gate !(bitset(MM_MIME8BIT, MimeMode) || 1900*7c478bd9Sstevel@tonic-gate (bitset(EF_IS_MIME, e->e_flags) && 1901*7c478bd9Sstevel@tonic-gate bitset(MM_CVTMIME, MimeMode))))) 1902*7c478bd9Sstevel@tonic-gate { 1903*7c478bd9Sstevel@tonic-gate e->e_status = "5.6.3"; 1904*7c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 1905*7c478bd9Sstevel@tonic-gate "554 Cannot send 8-bit data to 7-bit destination"); 1906*7c478bd9Sstevel@tonic-gate rcode = EX_DATAERR; 1907*7c478bd9Sstevel@tonic-gate goto give_up; 1908*7c478bd9Sstevel@tonic-gate } 1909*7c478bd9Sstevel@tonic-gate 1910*7c478bd9Sstevel@tonic-gate if (tTd(62, 8)) 1911*7c478bd9Sstevel@tonic-gate checkfds("before delivery"); 1912*7c478bd9Sstevel@tonic-gate 1913*7c478bd9Sstevel@tonic-gate /* check for Local Person Communication -- not for mortals!!! */ 1914*7c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[LPC]") == 0) 1915*7c478bd9Sstevel@tonic-gate { 1916*7c478bd9Sstevel@tonic-gate if (clever) 1917*7c478bd9Sstevel@tonic-gate { 1918*7c478bd9Sstevel@tonic-gate /* flush any expired connections */ 1919*7c478bd9Sstevel@tonic-gate (void) mci_scan(NULL); 1920*7c478bd9Sstevel@tonic-gate 1921*7c478bd9Sstevel@tonic-gate /* try to get a cached connection or just a slot */ 1922*7c478bd9Sstevel@tonic-gate mci = mci_get(m->m_name, m); 1923*7c478bd9Sstevel@tonic-gate if (mci->mci_host == NULL) 1924*7c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 1925*7c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 1926*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 1927*7c478bd9Sstevel@tonic-gate { 1928*7c478bd9Sstevel@tonic-gate message("Using cached SMTP/LPC connection for %s...", 1929*7c478bd9Sstevel@tonic-gate m->m_name); 1930*7c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 1931*7c478bd9Sstevel@tonic-gate goto do_transfer; 1932*7c478bd9Sstevel@tonic-gate } 1933*7c478bd9Sstevel@tonic-gate } 1934*7c478bd9Sstevel@tonic-gate else 1935*7c478bd9Sstevel@tonic-gate { 1936*7c478bd9Sstevel@tonic-gate mci = mci_new(e->e_rpool); 1937*7c478bd9Sstevel@tonic-gate } 1938*7c478bd9Sstevel@tonic-gate mci->mci_in = smioin; 1939*7c478bd9Sstevel@tonic-gate mci->mci_out = smioout; 1940*7c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 1941*7c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 1942*7c478bd9Sstevel@tonic-gate if (clever) 1943*7c478bd9Sstevel@tonic-gate { 1944*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 1945*7c478bd9Sstevel@tonic-gate mci_cache(mci); 1946*7c478bd9Sstevel@tonic-gate } 1947*7c478bd9Sstevel@tonic-gate else 1948*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 1949*7c478bd9Sstevel@tonic-gate } 1950*7c478bd9Sstevel@tonic-gate else if (strcmp(m->m_mailer, "[IPC]") == 0) 1951*7c478bd9Sstevel@tonic-gate { 1952*7c478bd9Sstevel@tonic-gate register int i; 1953*7c478bd9Sstevel@tonic-gate 1954*7c478bd9Sstevel@tonic-gate if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') 1955*7c478bd9Sstevel@tonic-gate { 1956*7c478bd9Sstevel@tonic-gate syserr("null destination for %s mailer", m->m_mailer); 1957*7c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 1958*7c478bd9Sstevel@tonic-gate goto give_up; 1959*7c478bd9Sstevel@tonic-gate } 1960*7c478bd9Sstevel@tonic-gate 1961*7c478bd9Sstevel@tonic-gate # if NETUNIX 1962*7c478bd9Sstevel@tonic-gate if (strcmp(pv[0], "FILE") == 0) 1963*7c478bd9Sstevel@tonic-gate { 1964*7c478bd9Sstevel@tonic-gate curhost = CurHostName = "localhost"; 1965*7c478bd9Sstevel@tonic-gate mux_path = pv[1]; 1966*7c478bd9Sstevel@tonic-gate } 1967*7c478bd9Sstevel@tonic-gate else 1968*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 1969*7c478bd9Sstevel@tonic-gate { 1970*7c478bd9Sstevel@tonic-gate CurHostName = pv[1]; 1971*7c478bd9Sstevel@tonic-gate curhost = hostsignature(m, pv[1]); 1972*7c478bd9Sstevel@tonic-gate } 1973*7c478bd9Sstevel@tonic-gate 1974*7c478bd9Sstevel@tonic-gate if (curhost == NULL || curhost[0] == '\0') 1975*7c478bd9Sstevel@tonic-gate { 1976*7c478bd9Sstevel@tonic-gate syserr("null host signature for %s", pv[1]); 1977*7c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 1978*7c478bd9Sstevel@tonic-gate goto give_up; 1979*7c478bd9Sstevel@tonic-gate } 1980*7c478bd9Sstevel@tonic-gate 1981*7c478bd9Sstevel@tonic-gate if (!clever) 1982*7c478bd9Sstevel@tonic-gate { 1983*7c478bd9Sstevel@tonic-gate syserr("554 5.3.5 non-clever IPC"); 1984*7c478bd9Sstevel@tonic-gate rcode = EX_CONFIG; 1985*7c478bd9Sstevel@tonic-gate goto give_up; 1986*7c478bd9Sstevel@tonic-gate } 1987*7c478bd9Sstevel@tonic-gate if (pv[2] != NULL 1988*7c478bd9Sstevel@tonic-gate # if NETUNIX 1989*7c478bd9Sstevel@tonic-gate && mux_path == NULL 1990*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 1991*7c478bd9Sstevel@tonic-gate ) 1992*7c478bd9Sstevel@tonic-gate { 1993*7c478bd9Sstevel@tonic-gate port = htons((unsigned short) atoi(pv[2])); 1994*7c478bd9Sstevel@tonic-gate if (port == 0) 1995*7c478bd9Sstevel@tonic-gate { 1996*7c478bd9Sstevel@tonic-gate # ifdef NO_GETSERVBYNAME 1997*7c478bd9Sstevel@tonic-gate syserr("Invalid port number: %s", pv[2]); 1998*7c478bd9Sstevel@tonic-gate # else /* NO_GETSERVBYNAME */ 1999*7c478bd9Sstevel@tonic-gate struct servent *sp = getservbyname(pv[2], "tcp"); 2000*7c478bd9Sstevel@tonic-gate 2001*7c478bd9Sstevel@tonic-gate if (sp == NULL) 2002*7c478bd9Sstevel@tonic-gate syserr("Service %s unknown", pv[2]); 2003*7c478bd9Sstevel@tonic-gate else 2004*7c478bd9Sstevel@tonic-gate port = sp->s_port; 2005*7c478bd9Sstevel@tonic-gate # endif /* NO_GETSERVBYNAME */ 2006*7c478bd9Sstevel@tonic-gate } 2007*7c478bd9Sstevel@tonic-gate } 2008*7c478bd9Sstevel@tonic-gate 2009*7c478bd9Sstevel@tonic-gate nummxhosts = parse_hostsignature(curhost, mxhosts, m); 2010*7c478bd9Sstevel@tonic-gate if (TimeOuts.to_aconnect > 0) 2011*7c478bd9Sstevel@tonic-gate enough = curtime() + TimeOuts.to_aconnect; 2012*7c478bd9Sstevel@tonic-gate tryhost: 2013*7c478bd9Sstevel@tonic-gate while (hostnum < nummxhosts) 2014*7c478bd9Sstevel@tonic-gate { 2015*7c478bd9Sstevel@tonic-gate char sep = ':'; 2016*7c478bd9Sstevel@tonic-gate char *endp; 2017*7c478bd9Sstevel@tonic-gate static char hostbuf[MAXNAME + 1]; 2018*7c478bd9Sstevel@tonic-gate bool tried_fallbacksmarthost = false; 2019*7c478bd9Sstevel@tonic-gate 2020*7c478bd9Sstevel@tonic-gate # if NETINET6 2021*7c478bd9Sstevel@tonic-gate if (*mxhosts[hostnum] == '[') 2022*7c478bd9Sstevel@tonic-gate { 2023*7c478bd9Sstevel@tonic-gate endp = strchr(mxhosts[hostnum] + 1, ']'); 2024*7c478bd9Sstevel@tonic-gate if (endp != NULL) 2025*7c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 2026*7c478bd9Sstevel@tonic-gate } 2027*7c478bd9Sstevel@tonic-gate else 2028*7c478bd9Sstevel@tonic-gate endp = strpbrk(mxhosts[hostnum], ":,"); 2029*7c478bd9Sstevel@tonic-gate # else /* NETINET6 */ 2030*7c478bd9Sstevel@tonic-gate endp = strpbrk(mxhosts[hostnum], ":,"); 2031*7c478bd9Sstevel@tonic-gate # endif /* NETINET6 */ 2032*7c478bd9Sstevel@tonic-gate if (endp != NULL) 2033*7c478bd9Sstevel@tonic-gate { 2034*7c478bd9Sstevel@tonic-gate sep = *endp; 2035*7c478bd9Sstevel@tonic-gate *endp = '\0'; 2036*7c478bd9Sstevel@tonic-gate } 2037*7c478bd9Sstevel@tonic-gate 2038*7c478bd9Sstevel@tonic-gate if (hostnum == 1 && skip_back != NULL) 2039*7c478bd9Sstevel@tonic-gate { 2040*7c478bd9Sstevel@tonic-gate /* 2041*7c478bd9Sstevel@tonic-gate ** Coattail piggybacking is no longer an 2042*7c478bd9Sstevel@tonic-gate ** option with the mail host next to be tried 2043*7c478bd9Sstevel@tonic-gate ** no longer the lowest MX preference 2044*7c478bd9Sstevel@tonic-gate ** (hostnum == 1 meaning we're on the second 2045*7c478bd9Sstevel@tonic-gate ** preference). We do not try to coattail 2046*7c478bd9Sstevel@tonic-gate ** piggyback more than the first MX preference. 2047*7c478bd9Sstevel@tonic-gate ** Revert 'tochain' to last location for 2048*7c478bd9Sstevel@tonic-gate ** coincidental piggybacking. This works this 2049*7c478bd9Sstevel@tonic-gate ** easily because the q_tchain kept getting 2050*7c478bd9Sstevel@tonic-gate ** added to the top of the linked list. 2051*7c478bd9Sstevel@tonic-gate */ 2052*7c478bd9Sstevel@tonic-gate 2053*7c478bd9Sstevel@tonic-gate tochain = skip_back; 2054*7c478bd9Sstevel@tonic-gate } 2055*7c478bd9Sstevel@tonic-gate 2056*7c478bd9Sstevel@tonic-gate if (*mxhosts[hostnum] == '\0') 2057*7c478bd9Sstevel@tonic-gate { 2058*7c478bd9Sstevel@tonic-gate syserr("deliver: null host name in signature"); 2059*7c478bd9Sstevel@tonic-gate hostnum++; 2060*7c478bd9Sstevel@tonic-gate if (endp != NULL) 2061*7c478bd9Sstevel@tonic-gate *endp = sep; 2062*7c478bd9Sstevel@tonic-gate continue; 2063*7c478bd9Sstevel@tonic-gate } 2064*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(hostbuf, mxhosts[hostnum], 2065*7c478bd9Sstevel@tonic-gate sizeof hostbuf); 2066*7c478bd9Sstevel@tonic-gate hostnum++; 2067*7c478bd9Sstevel@tonic-gate if (endp != NULL) 2068*7c478bd9Sstevel@tonic-gate *endp = sep; 2069*7c478bd9Sstevel@tonic-gate 2070*7c478bd9Sstevel@tonic-gate one_last_try: 2071*7c478bd9Sstevel@tonic-gate /* see if we already know that this host is fried */ 2072*7c478bd9Sstevel@tonic-gate CurHostName = hostbuf; 2073*7c478bd9Sstevel@tonic-gate mci = mci_get(hostbuf, m); 2074*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 2075*7c478bd9Sstevel@tonic-gate { 2076*7c478bd9Sstevel@tonic-gate char *type; 2077*7c478bd9Sstevel@tonic-gate 2078*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2079*7c478bd9Sstevel@tonic-gate { 2080*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: "); 2081*7c478bd9Sstevel@tonic-gate mci_dump(sm_debug_file(), mci, false); 2082*7c478bd9Sstevel@tonic-gate } 2083*7c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 2084*7c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 2085*7c478bd9Sstevel@tonic-gate type = "L"; 2086*7c478bd9Sstevel@tonic-gate else if (bitset(MCIF_ESMTP, mci->mci_flags)) 2087*7c478bd9Sstevel@tonic-gate type = "ES"; 2088*7c478bd9Sstevel@tonic-gate else 2089*7c478bd9Sstevel@tonic-gate type = "S"; 2090*7c478bd9Sstevel@tonic-gate message("Using cached %sMTP connection to %s via %s...", 2091*7c478bd9Sstevel@tonic-gate type, hostbuf, m->m_name); 2092*7c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 2093*7c478bd9Sstevel@tonic-gate break; 2094*7c478bd9Sstevel@tonic-gate } 2095*7c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 2096*7c478bd9Sstevel@tonic-gate if (mci->mci_exitstat != EX_OK) 2097*7c478bd9Sstevel@tonic-gate { 2098*7c478bd9Sstevel@tonic-gate if (mci->mci_exitstat == EX_TEMPFAIL) 2099*7c478bd9Sstevel@tonic-gate goodmxfound = true; 2100*7c478bd9Sstevel@tonic-gate 2101*7c478bd9Sstevel@tonic-gate /* Try FallbackSmartHost? */ 2102*7c478bd9Sstevel@tonic-gate if (should_try_fbsh(e, &tried_fallbacksmarthost, 2103*7c478bd9Sstevel@tonic-gate hostbuf, sizeof hostbuf, 2104*7c478bd9Sstevel@tonic-gate mci->mci_exitstat)) 2105*7c478bd9Sstevel@tonic-gate goto one_last_try; 2106*7c478bd9Sstevel@tonic-gate 2107*7c478bd9Sstevel@tonic-gate continue; 2108*7c478bd9Sstevel@tonic-gate } 2109*7c478bd9Sstevel@tonic-gate 2110*7c478bd9Sstevel@tonic-gate if (mci_lock_host(mci) != EX_OK) 2111*7c478bd9Sstevel@tonic-gate { 2112*7c478bd9Sstevel@tonic-gate mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); 2113*7c478bd9Sstevel@tonic-gate goodmxfound = true; 2114*7c478bd9Sstevel@tonic-gate continue; 2115*7c478bd9Sstevel@tonic-gate } 2116*7c478bd9Sstevel@tonic-gate 2117*7c478bd9Sstevel@tonic-gate /* try the connection */ 2118*7c478bd9Sstevel@tonic-gate sm_setproctitle(true, e, "%s %s: %s", 2119*7c478bd9Sstevel@tonic-gate qid_printname(e), 2120*7c478bd9Sstevel@tonic-gate hostbuf, "user open"); 2121*7c478bd9Sstevel@tonic-gate # if NETUNIX 2122*7c478bd9Sstevel@tonic-gate if (mux_path != NULL) 2123*7c478bd9Sstevel@tonic-gate { 2124*7c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", 2125*7c478bd9Sstevel@tonic-gate mux_path, m->m_name); 2126*7c478bd9Sstevel@tonic-gate i = makeconnection_ds((char *) mux_path, mci); 2127*7c478bd9Sstevel@tonic-gate } 2128*7c478bd9Sstevel@tonic-gate else 2129*7c478bd9Sstevel@tonic-gate # endif /* NETUNIX */ 2130*7c478bd9Sstevel@tonic-gate { 2131*7c478bd9Sstevel@tonic-gate if (port == 0) 2132*7c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", 2133*7c478bd9Sstevel@tonic-gate hostbuf, m->m_name); 2134*7c478bd9Sstevel@tonic-gate else 2135*7c478bd9Sstevel@tonic-gate message("Connecting to %s port %d via %s...", 2136*7c478bd9Sstevel@tonic-gate hostbuf, ntohs(port), 2137*7c478bd9Sstevel@tonic-gate m->m_name); 2138*7c478bd9Sstevel@tonic-gate i = makeconnection(hostbuf, port, mci, e, 2139*7c478bd9Sstevel@tonic-gate enough); 2140*7c478bd9Sstevel@tonic-gate } 2141*7c478bd9Sstevel@tonic-gate mci->mci_errno = errno; 2142*7c478bd9Sstevel@tonic-gate mci->mci_lastuse = curtime(); 2143*7c478bd9Sstevel@tonic-gate mci->mci_deliveries = 0; 2144*7c478bd9Sstevel@tonic-gate mci->mci_exitstat = i; 2145*7c478bd9Sstevel@tonic-gate # if NAMED_BIND 2146*7c478bd9Sstevel@tonic-gate mci->mci_herrno = h_errno; 2147*7c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */ 2148*7c478bd9Sstevel@tonic-gate 2149*7c478bd9Sstevel@tonic-gate /* 2150*7c478bd9Sstevel@tonic-gate ** Have we tried long enough to get a connection? 2151*7c478bd9Sstevel@tonic-gate ** If yes, skip to the fallback MX hosts 2152*7c478bd9Sstevel@tonic-gate ** (if existent). 2153*7c478bd9Sstevel@tonic-gate */ 2154*7c478bd9Sstevel@tonic-gate 2155*7c478bd9Sstevel@tonic-gate if (enough > 0 && mci->mci_lastuse >= enough) 2156*7c478bd9Sstevel@tonic-gate { 2157*7c478bd9Sstevel@tonic-gate int h; 2158*7c478bd9Sstevel@tonic-gate # if NAMED_BIND 2159*7c478bd9Sstevel@tonic-gate extern int NumFallbackMXHosts; 2160*7c478bd9Sstevel@tonic-gate # else /* NAMED_BIND */ 2161*7c478bd9Sstevel@tonic-gate const int NumFallbackMXHosts = 0; 2162*7c478bd9Sstevel@tonic-gate # endif /* NAMED_BIND */ 2163*7c478bd9Sstevel@tonic-gate 2164*7c478bd9Sstevel@tonic-gate if (hostnum < nummxhosts && LogLevel > 9) 2165*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2166*7c478bd9Sstevel@tonic-gate "Timeout.to_aconnect occurred before exhausting all addresses"); 2167*7c478bd9Sstevel@tonic-gate 2168*7c478bd9Sstevel@tonic-gate /* turn off timeout if fallback available */ 2169*7c478bd9Sstevel@tonic-gate if (NumFallbackMXHosts > 0) 2170*7c478bd9Sstevel@tonic-gate enough = 0; 2171*7c478bd9Sstevel@tonic-gate 2172*7c478bd9Sstevel@tonic-gate /* skip to a fallback MX host */ 2173*7c478bd9Sstevel@tonic-gate h = nummxhosts - NumFallbackMXHosts; 2174*7c478bd9Sstevel@tonic-gate if (hostnum < h) 2175*7c478bd9Sstevel@tonic-gate hostnum = h; 2176*7c478bd9Sstevel@tonic-gate } 2177*7c478bd9Sstevel@tonic-gate if (i == EX_OK) 2178*7c478bd9Sstevel@tonic-gate { 2179*7c478bd9Sstevel@tonic-gate goodmxfound = true; 2180*7c478bd9Sstevel@tonic-gate markstats(e, firstto, STATS_CONNECT); 2181*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 2182*7c478bd9Sstevel@tonic-gate mci_cache(mci); 2183*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 2184*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 2185*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 2186*7c478bd9Sstevel@tonic-gate "%05d === CONNECT %s\n", 2187*7c478bd9Sstevel@tonic-gate (int) CurrentPid, 2188*7c478bd9Sstevel@tonic-gate hostbuf); 2189*7c478bd9Sstevel@tonic-gate break; 2190*7c478bd9Sstevel@tonic-gate } 2191*7c478bd9Sstevel@tonic-gate else 2192*7c478bd9Sstevel@tonic-gate { 2193*7c478bd9Sstevel@tonic-gate /* Try FallbackSmartHost? */ 2194*7c478bd9Sstevel@tonic-gate if (should_try_fbsh(e, &tried_fallbacksmarthost, 2195*7c478bd9Sstevel@tonic-gate hostbuf, sizeof hostbuf, i)) 2196*7c478bd9Sstevel@tonic-gate goto one_last_try; 2197*7c478bd9Sstevel@tonic-gate 2198*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2199*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: makeconnection => stat=%d, errno=%d\n", 2200*7c478bd9Sstevel@tonic-gate i, errno); 2201*7c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 2202*7c478bd9Sstevel@tonic-gate goodmxfound = true; 2203*7c478bd9Sstevel@tonic-gate mci_unlock_host(mci); 2204*7c478bd9Sstevel@tonic-gate } 2205*7c478bd9Sstevel@tonic-gate 2206*7c478bd9Sstevel@tonic-gate /* enter status of this host */ 2207*7c478bd9Sstevel@tonic-gate setstat(i); 2208*7c478bd9Sstevel@tonic-gate 2209*7c478bd9Sstevel@tonic-gate /* should print some message here for -v mode */ 2210*7c478bd9Sstevel@tonic-gate } 2211*7c478bd9Sstevel@tonic-gate if (mci == NULL) 2212*7c478bd9Sstevel@tonic-gate { 2213*7c478bd9Sstevel@tonic-gate syserr("deliver: no host name"); 2214*7c478bd9Sstevel@tonic-gate rcode = EX_SOFTWARE; 2215*7c478bd9Sstevel@tonic-gate goto give_up; 2216*7c478bd9Sstevel@tonic-gate } 2217*7c478bd9Sstevel@tonic-gate mci->mci_pid = 0; 2218*7c478bd9Sstevel@tonic-gate } 2219*7c478bd9Sstevel@tonic-gate else 2220*7c478bd9Sstevel@tonic-gate { 2221*7c478bd9Sstevel@tonic-gate /* flush any expired connections */ 2222*7c478bd9Sstevel@tonic-gate (void) mci_scan(NULL); 2223*7c478bd9Sstevel@tonic-gate mci = NULL; 2224*7c478bd9Sstevel@tonic-gate 2225*7c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 2226*7c478bd9Sstevel@tonic-gate { 2227*7c478bd9Sstevel@tonic-gate /* try to get a cached connection */ 2228*7c478bd9Sstevel@tonic-gate mci = mci_get(m->m_name, m); 2229*7c478bd9Sstevel@tonic-gate if (mci->mci_host == NULL) 2230*7c478bd9Sstevel@tonic-gate mci->mci_host = m->m_name; 2231*7c478bd9Sstevel@tonic-gate CurHostName = mci->mci_host; 2232*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED) 2233*7c478bd9Sstevel@tonic-gate { 2234*7c478bd9Sstevel@tonic-gate message("Using cached LMTP connection for %s...", 2235*7c478bd9Sstevel@tonic-gate m->m_name); 2236*7c478bd9Sstevel@tonic-gate mci->mci_deliveries++; 2237*7c478bd9Sstevel@tonic-gate goto do_transfer; 2238*7c478bd9Sstevel@tonic-gate } 2239*7c478bd9Sstevel@tonic-gate } 2240*7c478bd9Sstevel@tonic-gate 2241*7c478bd9Sstevel@tonic-gate /* announce the connection to verbose listeners */ 2242*7c478bd9Sstevel@tonic-gate if (host == NULL || host[0] == '\0') 2243*7c478bd9Sstevel@tonic-gate message("Connecting to %s...", m->m_name); 2244*7c478bd9Sstevel@tonic-gate else 2245*7c478bd9Sstevel@tonic-gate message("Connecting to %s via %s...", host, m->m_name); 2246*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 2247*7c478bd9Sstevel@tonic-gate { 2248*7c478bd9Sstevel@tonic-gate char **av; 2249*7c478bd9Sstevel@tonic-gate 2250*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 2251*7c478bd9Sstevel@tonic-gate "%05d === EXEC", (int) CurrentPid); 2252*7c478bd9Sstevel@tonic-gate for (av = pv; *av != NULL; av++) 2253*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 2254*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, " %s", 2255*7c478bd9Sstevel@tonic-gate *av); 2256*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT, 2257*7c478bd9Sstevel@tonic-gate "\n"); 2258*7c478bd9Sstevel@tonic-gate } 2259*7c478bd9Sstevel@tonic-gate 2260*7c478bd9Sstevel@tonic-gate #if XDEBUG 2261*7c478bd9Sstevel@tonic-gate checkfd012("before creating mail pipe"); 2262*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 2263*7c478bd9Sstevel@tonic-gate 2264*7c478bd9Sstevel@tonic-gate /* create a pipe to shove the mail through */ 2265*7c478bd9Sstevel@tonic-gate if (pipe(mpvect) < 0) 2266*7c478bd9Sstevel@tonic-gate { 2267*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): pipe (to mailer)", 2268*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 2269*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2270*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 2271*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2272*7c478bd9Sstevel@tonic-gate goto give_up; 2273*7c478bd9Sstevel@tonic-gate } 2274*7c478bd9Sstevel@tonic-gate 2275*7c478bd9Sstevel@tonic-gate #if XDEBUG 2276*7c478bd9Sstevel@tonic-gate /* make sure we didn't get one of the standard I/O files */ 2277*7c478bd9Sstevel@tonic-gate if (mpvect[0] < 3 || mpvect[1] < 3) 2278*7c478bd9Sstevel@tonic-gate { 2279*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): bogus mpvect %d %d", 2280*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name, 2281*7c478bd9Sstevel@tonic-gate mpvect[0], mpvect[1]); 2282*7c478bd9Sstevel@tonic-gate printopenfds(true); 2283*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2284*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 2285*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2286*7c478bd9Sstevel@tonic-gate goto give_up; 2287*7c478bd9Sstevel@tonic-gate } 2288*7c478bd9Sstevel@tonic-gate 2289*7c478bd9Sstevel@tonic-gate /* make sure system call isn't dead meat */ 2290*7c478bd9Sstevel@tonic-gate checkfdopen(mpvect[0], "mpvect[0]"); 2291*7c478bd9Sstevel@tonic-gate checkfdopen(mpvect[1], "mpvect[1]"); 2292*7c478bd9Sstevel@tonic-gate if (mpvect[0] == mpvect[1] || 2293*7c478bd9Sstevel@tonic-gate (e->e_lockfp != NULL && 2294*7c478bd9Sstevel@tonic-gate (mpvect[0] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 2295*7c478bd9Sstevel@tonic-gate NULL) || 2296*7c478bd9Sstevel@tonic-gate mpvect[1] == sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 2297*7c478bd9Sstevel@tonic-gate NULL)))) 2298*7c478bd9Sstevel@tonic-gate { 2299*7c478bd9Sstevel@tonic-gate if (e->e_lockfp == NULL) 2300*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): overlapping mpvect %d %d", 2301*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2302*7c478bd9Sstevel@tonic-gate m->m_name, mpvect[0], mpvect[1]); 2303*7c478bd9Sstevel@tonic-gate else 2304*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", 2305*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2306*7c478bd9Sstevel@tonic-gate m->m_name, mpvect[0], mpvect[1], 2307*7c478bd9Sstevel@tonic-gate sm_io_getinfo(e->e_lockfp, 2308*7c478bd9Sstevel@tonic-gate SM_IO_WHAT_FD, NULL)); 2309*7c478bd9Sstevel@tonic-gate } 2310*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 2311*7c478bd9Sstevel@tonic-gate 2312*7c478bd9Sstevel@tonic-gate /* create a return pipe */ 2313*7c478bd9Sstevel@tonic-gate if (pipe(rpvect) < 0) 2314*7c478bd9Sstevel@tonic-gate { 2315*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): pipe (from mailer)", 2316*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2317*7c478bd9Sstevel@tonic-gate m->m_name); 2318*7c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 2319*7c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 2320*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2321*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 2322*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2323*7c478bd9Sstevel@tonic-gate goto give_up; 2324*7c478bd9Sstevel@tonic-gate } 2325*7c478bd9Sstevel@tonic-gate #if XDEBUG 2326*7c478bd9Sstevel@tonic-gate checkfdopen(rpvect[0], "rpvect[0]"); 2327*7c478bd9Sstevel@tonic-gate checkfdopen(rpvect[1], "rpvect[1]"); 2328*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 2329*7c478bd9Sstevel@tonic-gate 2330*7c478bd9Sstevel@tonic-gate /* 2331*7c478bd9Sstevel@tonic-gate ** Actually fork the mailer process. 2332*7c478bd9Sstevel@tonic-gate ** DOFORK is clever about retrying. 2333*7c478bd9Sstevel@tonic-gate ** 2334*7c478bd9Sstevel@tonic-gate ** Dispose of SIGCHLD signal catchers that may be laying 2335*7c478bd9Sstevel@tonic-gate ** around so that endmailer will get it. 2336*7c478bd9Sstevel@tonic-gate */ 2337*7c478bd9Sstevel@tonic-gate 2338*7c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) /* for debugging */ 2339*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 2340*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 2341*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 2342*7c478bd9Sstevel@tonic-gate 2343*7c478bd9Sstevel@tonic-gate 2344*7c478bd9Sstevel@tonic-gate DOFORK(FORK); 2345*7c478bd9Sstevel@tonic-gate /* pid is set by DOFORK */ 2346*7c478bd9Sstevel@tonic-gate 2347*7c478bd9Sstevel@tonic-gate if (pid < 0) 2348*7c478bd9Sstevel@tonic-gate { 2349*7c478bd9Sstevel@tonic-gate /* failure */ 2350*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot fork", 2351*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), m->m_name); 2352*7c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 2353*7c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 2354*7c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 2355*7c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 2356*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 2357*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: NULL\n"); 2358*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2359*7c478bd9Sstevel@tonic-gate goto give_up; 2360*7c478bd9Sstevel@tonic-gate } 2361*7c478bd9Sstevel@tonic-gate else if (pid == 0) 2362*7c478bd9Sstevel@tonic-gate { 2363*7c478bd9Sstevel@tonic-gate int save_errno; 2364*7c478bd9Sstevel@tonic-gate int sff; 2365*7c478bd9Sstevel@tonic-gate int new_euid = NO_UID; 2366*7c478bd9Sstevel@tonic-gate int new_ruid = NO_UID; 2367*7c478bd9Sstevel@tonic-gate int new_gid = NO_GID; 2368*7c478bd9Sstevel@tonic-gate char *user = NULL; 2369*7c478bd9Sstevel@tonic-gate struct stat stb; 2370*7c478bd9Sstevel@tonic-gate extern int DtableSize; 2371*7c478bd9Sstevel@tonic-gate 2372*7c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 2373*7c478bd9Sstevel@tonic-gate 2374*7c478bd9Sstevel@tonic-gate /* clear the events to turn off SIGALRMs */ 2375*7c478bd9Sstevel@tonic-gate sm_clear_events(); 2376*7c478bd9Sstevel@tonic-gate 2377*7c478bd9Sstevel@tonic-gate /* Reset global flags */ 2378*7c478bd9Sstevel@tonic-gate RestartRequest = NULL; 2379*7c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 2380*7c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 2381*7c478bd9Sstevel@tonic-gate PendingSignal = 0; 2382*7c478bd9Sstevel@tonic-gate 2383*7c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 2384*7c478bd9Sstevel@tonic-gate (void) close(sm_io_getinfo(e->e_lockfp, 2385*7c478bd9Sstevel@tonic-gate SM_IO_WHAT_FD, 2386*7c478bd9Sstevel@tonic-gate NULL)); 2387*7c478bd9Sstevel@tonic-gate 2388*7c478bd9Sstevel@tonic-gate /* child -- set up input & exec mailer */ 2389*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGALRM, sm_signal_noop); 2390*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 2391*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGHUP, SIG_IGN); 2392*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGINT, SIG_IGN); 2393*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 2394*7c478bd9Sstevel@tonic-gate # ifdef SIGUSR1 2395*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGUSR1, sm_signal_noop); 2396*7c478bd9Sstevel@tonic-gate # endif /* SIGUSR1 */ 2397*7c478bd9Sstevel@tonic-gate 2398*7c478bd9Sstevel@tonic-gate if (m != FileMailer || stat(tochain->q_user, &stb) < 0) 2399*7c478bd9Sstevel@tonic-gate stb.st_mode = 0; 2400*7c478bd9Sstevel@tonic-gate 2401*7c478bd9Sstevel@tonic-gate # if HASSETUSERCONTEXT 2402*7c478bd9Sstevel@tonic-gate /* 2403*7c478bd9Sstevel@tonic-gate ** Set user resources. 2404*7c478bd9Sstevel@tonic-gate */ 2405*7c478bd9Sstevel@tonic-gate 2406*7c478bd9Sstevel@tonic-gate if (contextaddr != NULL) 2407*7c478bd9Sstevel@tonic-gate { 2408*7c478bd9Sstevel@tonic-gate int sucflags; 2409*7c478bd9Sstevel@tonic-gate struct passwd *pwd; 2410*7c478bd9Sstevel@tonic-gate 2411*7c478bd9Sstevel@tonic-gate if (contextaddr->q_ruser != NULL) 2412*7c478bd9Sstevel@tonic-gate pwd = sm_getpwnam(contextaddr->q_ruser); 2413*7c478bd9Sstevel@tonic-gate else 2414*7c478bd9Sstevel@tonic-gate pwd = sm_getpwnam(contextaddr->q_user); 2415*7c478bd9Sstevel@tonic-gate sucflags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY; 2416*7c478bd9Sstevel@tonic-gate #ifdef LOGIN_SETMAC 2417*7c478bd9Sstevel@tonic-gate sucflags |= LOGIN_SETMAC; 2418*7c478bd9Sstevel@tonic-gate #endif /* LOGIN_SETMAC */ 2419*7c478bd9Sstevel@tonic-gate if (pwd != NULL && 2420*7c478bd9Sstevel@tonic-gate setusercontext(NULL, pwd, pwd->pw_uid, 2421*7c478bd9Sstevel@tonic-gate sucflags) == -1 && 2422*7c478bd9Sstevel@tonic-gate suidwarn) 2423*7c478bd9Sstevel@tonic-gate { 2424*7c478bd9Sstevel@tonic-gate syserr("openmailer: setusercontext() failed"); 2425*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2426*7c478bd9Sstevel@tonic-gate } 2427*7c478bd9Sstevel@tonic-gate } 2428*7c478bd9Sstevel@tonic-gate # endif /* HASSETUSERCONTEXT */ 2429*7c478bd9Sstevel@tonic-gate 2430*7c478bd9Sstevel@tonic-gate #if HASNICE 2431*7c478bd9Sstevel@tonic-gate /* tweak niceness */ 2432*7c478bd9Sstevel@tonic-gate if (m->m_nice != 0) 2433*7c478bd9Sstevel@tonic-gate (void) nice(m->m_nice); 2434*7c478bd9Sstevel@tonic-gate #endif /* HASNICE */ 2435*7c478bd9Sstevel@tonic-gate 2436*7c478bd9Sstevel@tonic-gate /* reset group id */ 2437*7c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2438*7c478bd9Sstevel@tonic-gate { 2439*7c478bd9Sstevel@tonic-gate if (m->m_gid == NO_GID) 2440*7c478bd9Sstevel@tonic-gate new_gid = RunAsGid; 2441*7c478bd9Sstevel@tonic-gate else 2442*7c478bd9Sstevel@tonic-gate new_gid = m->m_gid; 2443*7c478bd9Sstevel@tonic-gate } 2444*7c478bd9Sstevel@tonic-gate else if (bitset(S_ISGID, stb.st_mode)) 2445*7c478bd9Sstevel@tonic-gate new_gid = stb.st_gid; 2446*7c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_gid != 0) 2447*7c478bd9Sstevel@tonic-gate { 2448*7c478bd9Sstevel@tonic-gate if (!DontInitGroups) 2449*7c478bd9Sstevel@tonic-gate { 2450*7c478bd9Sstevel@tonic-gate user = ctladdr->q_ruser; 2451*7c478bd9Sstevel@tonic-gate if (user == NULL) 2452*7c478bd9Sstevel@tonic-gate user = ctladdr->q_user; 2453*7c478bd9Sstevel@tonic-gate 2454*7c478bd9Sstevel@tonic-gate if (initgroups(user, 2455*7c478bd9Sstevel@tonic-gate ctladdr->q_gid) == -1 2456*7c478bd9Sstevel@tonic-gate && suidwarn) 2457*7c478bd9Sstevel@tonic-gate { 2458*7c478bd9Sstevel@tonic-gate syserr("openmailer: initgroups(%s, %d) failed", 2459*7c478bd9Sstevel@tonic-gate user, ctladdr->q_gid); 2460*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2461*7c478bd9Sstevel@tonic-gate } 2462*7c478bd9Sstevel@tonic-gate } 2463*7c478bd9Sstevel@tonic-gate else 2464*7c478bd9Sstevel@tonic-gate { 2465*7c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 2466*7c478bd9Sstevel@tonic-gate 2467*7c478bd9Sstevel@tonic-gate gidset[0] = ctladdr->q_gid; 2468*7c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 2469*7c478bd9Sstevel@tonic-gate && suidwarn) 2470*7c478bd9Sstevel@tonic-gate { 2471*7c478bd9Sstevel@tonic-gate syserr("openmailer: setgroups() failed"); 2472*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2473*7c478bd9Sstevel@tonic-gate } 2474*7c478bd9Sstevel@tonic-gate } 2475*7c478bd9Sstevel@tonic-gate new_gid = ctladdr->q_gid; 2476*7c478bd9Sstevel@tonic-gate } 2477*7c478bd9Sstevel@tonic-gate else 2478*7c478bd9Sstevel@tonic-gate { 2479*7c478bd9Sstevel@tonic-gate if (!DontInitGroups) 2480*7c478bd9Sstevel@tonic-gate { 2481*7c478bd9Sstevel@tonic-gate user = DefUser; 2482*7c478bd9Sstevel@tonic-gate if (initgroups(DefUser, DefGid) == -1 && 2483*7c478bd9Sstevel@tonic-gate suidwarn) 2484*7c478bd9Sstevel@tonic-gate { 2485*7c478bd9Sstevel@tonic-gate syserr("openmailer: initgroups(%s, %d) failed", 2486*7c478bd9Sstevel@tonic-gate DefUser, DefGid); 2487*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2488*7c478bd9Sstevel@tonic-gate } 2489*7c478bd9Sstevel@tonic-gate } 2490*7c478bd9Sstevel@tonic-gate else 2491*7c478bd9Sstevel@tonic-gate { 2492*7c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 2493*7c478bd9Sstevel@tonic-gate 2494*7c478bd9Sstevel@tonic-gate gidset[0] = DefGid; 2495*7c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 2496*7c478bd9Sstevel@tonic-gate && suidwarn) 2497*7c478bd9Sstevel@tonic-gate { 2498*7c478bd9Sstevel@tonic-gate syserr("openmailer: setgroups() failed"); 2499*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2500*7c478bd9Sstevel@tonic-gate } 2501*7c478bd9Sstevel@tonic-gate } 2502*7c478bd9Sstevel@tonic-gate if (m->m_gid == NO_GID) 2503*7c478bd9Sstevel@tonic-gate new_gid = DefGid; 2504*7c478bd9Sstevel@tonic-gate else 2505*7c478bd9Sstevel@tonic-gate new_gid = m->m_gid; 2506*7c478bd9Sstevel@tonic-gate } 2507*7c478bd9Sstevel@tonic-gate if (new_gid != NO_GID) 2508*7c478bd9Sstevel@tonic-gate { 2509*7c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && 2510*7c478bd9Sstevel@tonic-gate bitnset(M_SPECIFIC_UID, m->m_flags) && 2511*7c478bd9Sstevel@tonic-gate new_gid != getgid() && 2512*7c478bd9Sstevel@tonic-gate new_gid != getegid()) 2513*7c478bd9Sstevel@tonic-gate { 2514*7c478bd9Sstevel@tonic-gate /* Only root can change the gid */ 2515*7c478bd9Sstevel@tonic-gate syserr("openmailer: insufficient privileges to change gid, RunAsUid=%d, new_gid=%d, gid=%d, egid=%d", 2516*7c478bd9Sstevel@tonic-gate (int) RunAsUid, (int) new_gid, 2517*7c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 2518*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2519*7c478bd9Sstevel@tonic-gate } 2520*7c478bd9Sstevel@tonic-gate 2521*7c478bd9Sstevel@tonic-gate if (setgid(new_gid) < 0 && suidwarn) 2522*7c478bd9Sstevel@tonic-gate { 2523*7c478bd9Sstevel@tonic-gate syserr("openmailer: setgid(%ld) failed", 2524*7c478bd9Sstevel@tonic-gate (long) new_gid); 2525*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2526*7c478bd9Sstevel@tonic-gate } 2527*7c478bd9Sstevel@tonic-gate } 2528*7c478bd9Sstevel@tonic-gate 2529*7c478bd9Sstevel@tonic-gate /* change root to some "safe" directory */ 2530*7c478bd9Sstevel@tonic-gate if (m->m_rootdir != NULL) 2531*7c478bd9Sstevel@tonic-gate { 2532*7c478bd9Sstevel@tonic-gate expand(m->m_rootdir, cbuf, sizeof cbuf, e); 2533*7c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 2534*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: chroot %s\n", 2535*7c478bd9Sstevel@tonic-gate cbuf); 2536*7c478bd9Sstevel@tonic-gate if (chroot(cbuf) < 0) 2537*7c478bd9Sstevel@tonic-gate { 2538*7c478bd9Sstevel@tonic-gate syserr("openmailer: Cannot chroot(%s)", 2539*7c478bd9Sstevel@tonic-gate cbuf); 2540*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2541*7c478bd9Sstevel@tonic-gate } 2542*7c478bd9Sstevel@tonic-gate if (chdir("/") < 0) 2543*7c478bd9Sstevel@tonic-gate { 2544*7c478bd9Sstevel@tonic-gate syserr("openmailer: cannot chdir(/)"); 2545*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2546*7c478bd9Sstevel@tonic-gate } 2547*7c478bd9Sstevel@tonic-gate } 2548*7c478bd9Sstevel@tonic-gate 2549*7c478bd9Sstevel@tonic-gate /* reset user id */ 2550*7c478bd9Sstevel@tonic-gate endpwent(); 2551*7c478bd9Sstevel@tonic-gate sm_mbdb_terminate(); 2552*7c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, m->m_flags)) 2553*7c478bd9Sstevel@tonic-gate { 2554*7c478bd9Sstevel@tonic-gate if (m->m_uid == NO_UID) 2555*7c478bd9Sstevel@tonic-gate new_euid = RunAsUid; 2556*7c478bd9Sstevel@tonic-gate else 2557*7c478bd9Sstevel@tonic-gate new_euid = m->m_uid; 2558*7c478bd9Sstevel@tonic-gate 2559*7c478bd9Sstevel@tonic-gate /* 2560*7c478bd9Sstevel@tonic-gate ** Undo the effects of the uid change in main 2561*7c478bd9Sstevel@tonic-gate ** for signal handling. The real uid may 2562*7c478bd9Sstevel@tonic-gate ** be used by mailer in adding a "From " 2563*7c478bd9Sstevel@tonic-gate ** line. 2564*7c478bd9Sstevel@tonic-gate */ 2565*7c478bd9Sstevel@tonic-gate 2566*7c478bd9Sstevel@tonic-gate if (RealUid != 0 && RealUid != getuid()) 2567*7c478bd9Sstevel@tonic-gate { 2568*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID 2569*7c478bd9Sstevel@tonic-gate # if HASSETREUID 2570*7c478bd9Sstevel@tonic-gate if (setreuid(RealUid, geteuid()) < 0) 2571*7c478bd9Sstevel@tonic-gate { 2572*7c478bd9Sstevel@tonic-gate syserr("openmailer: setreuid(%d, %d) failed", 2573*7c478bd9Sstevel@tonic-gate (int) RealUid, (int) geteuid()); 2574*7c478bd9Sstevel@tonic-gate exit(EX_OSERR); 2575*7c478bd9Sstevel@tonic-gate } 2576*7c478bd9Sstevel@tonic-gate # endif /* HASSETREUID */ 2577*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 2578*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID 2579*7c478bd9Sstevel@tonic-gate new_ruid = RealUid; 2580*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 2581*7c478bd9Sstevel@tonic-gate } 2582*7c478bd9Sstevel@tonic-gate } 2583*7c478bd9Sstevel@tonic-gate else if (bitset(S_ISUID, stb.st_mode)) 2584*7c478bd9Sstevel@tonic-gate new_ruid = stb.st_uid; 2585*7c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 2586*7c478bd9Sstevel@tonic-gate new_ruid = ctladdr->q_uid; 2587*7c478bd9Sstevel@tonic-gate else if (m->m_uid != NO_UID) 2588*7c478bd9Sstevel@tonic-gate new_ruid = m->m_uid; 2589*7c478bd9Sstevel@tonic-gate else 2590*7c478bd9Sstevel@tonic-gate new_ruid = DefUid; 2591*7c478bd9Sstevel@tonic-gate 2592*7c478bd9Sstevel@tonic-gate # if _FFR_USE_SETLOGIN 2593*7c478bd9Sstevel@tonic-gate /* run disconnected from terminal and set login name */ 2594*7c478bd9Sstevel@tonic-gate if (setsid() >= 0 && 2595*7c478bd9Sstevel@tonic-gate ctladdr != NULL && ctladdr->q_uid != 0 && 2596*7c478bd9Sstevel@tonic-gate new_euid == ctladdr->q_uid) 2597*7c478bd9Sstevel@tonic-gate { 2598*7c478bd9Sstevel@tonic-gate struct passwd *pwd; 2599*7c478bd9Sstevel@tonic-gate 2600*7c478bd9Sstevel@tonic-gate pwd = sm_getpwuid(ctladdr->q_uid); 2601*7c478bd9Sstevel@tonic-gate if (pwd != NULL && suidwarn) 2602*7c478bd9Sstevel@tonic-gate (void) setlogin(pwd->pw_name); 2603*7c478bd9Sstevel@tonic-gate endpwent(); 2604*7c478bd9Sstevel@tonic-gate } 2605*7c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SETLOGIN */ 2606*7c478bd9Sstevel@tonic-gate 2607*7c478bd9Sstevel@tonic-gate if (new_euid != NO_UID) 2608*7c478bd9Sstevel@tonic-gate { 2609*7c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && new_euid != RunAsUid) 2610*7c478bd9Sstevel@tonic-gate { 2611*7c478bd9Sstevel@tonic-gate /* Only root can change the uid */ 2612*7c478bd9Sstevel@tonic-gate syserr("openmailer: insufficient privileges to change uid, new_euid=%d, RunAsUid=%d", 2613*7c478bd9Sstevel@tonic-gate (int) new_euid, (int) RunAsUid); 2614*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2615*7c478bd9Sstevel@tonic-gate } 2616*7c478bd9Sstevel@tonic-gate 2617*7c478bd9Sstevel@tonic-gate vendor_set_uid(new_euid); 2618*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETEUID 2619*7c478bd9Sstevel@tonic-gate if (seteuid(new_euid) < 0 && suidwarn) 2620*7c478bd9Sstevel@tonic-gate { 2621*7c478bd9Sstevel@tonic-gate syserr("openmailer: seteuid(%ld) failed", 2622*7c478bd9Sstevel@tonic-gate (long) new_euid); 2623*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2624*7c478bd9Sstevel@tonic-gate } 2625*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETEUID */ 2626*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETREUID 2627*7c478bd9Sstevel@tonic-gate if (setreuid(new_ruid, new_euid) < 0 && suidwarn) 2628*7c478bd9Sstevel@tonic-gate { 2629*7c478bd9Sstevel@tonic-gate syserr("openmailer: setreuid(%ld, %ld) failed", 2630*7c478bd9Sstevel@tonic-gate (long) new_ruid, (long) new_euid); 2631*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2632*7c478bd9Sstevel@tonic-gate } 2633*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETREUID */ 2634*7c478bd9Sstevel@tonic-gate # if MAILER_SETUID_METHOD == USE_SETUID 2635*7c478bd9Sstevel@tonic-gate if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) 2636*7c478bd9Sstevel@tonic-gate { 2637*7c478bd9Sstevel@tonic-gate syserr("openmailer: setuid(%ld) failed", 2638*7c478bd9Sstevel@tonic-gate (long) new_euid); 2639*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2640*7c478bd9Sstevel@tonic-gate } 2641*7c478bd9Sstevel@tonic-gate # endif /* MAILER_SETUID_METHOD == USE_SETUID */ 2642*7c478bd9Sstevel@tonic-gate } 2643*7c478bd9Sstevel@tonic-gate else if (new_ruid != NO_UID) 2644*7c478bd9Sstevel@tonic-gate { 2645*7c478bd9Sstevel@tonic-gate vendor_set_uid(new_ruid); 2646*7c478bd9Sstevel@tonic-gate if (setuid(new_ruid) < 0 && suidwarn) 2647*7c478bd9Sstevel@tonic-gate { 2648*7c478bd9Sstevel@tonic-gate syserr("openmailer: setuid(%ld) failed", 2649*7c478bd9Sstevel@tonic-gate (long) new_ruid); 2650*7c478bd9Sstevel@tonic-gate exit(EX_TEMPFAIL); 2651*7c478bd9Sstevel@tonic-gate } 2652*7c478bd9Sstevel@tonic-gate } 2653*7c478bd9Sstevel@tonic-gate 2654*7c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 2655*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: running as r/euid=%d/%d, r/egid=%d/%d\n", 2656*7c478bd9Sstevel@tonic-gate (int) getuid(), (int) geteuid(), 2657*7c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 2658*7c478bd9Sstevel@tonic-gate 2659*7c478bd9Sstevel@tonic-gate /* move into some "safe" directory */ 2660*7c478bd9Sstevel@tonic-gate if (m->m_execdir != NULL) 2661*7c478bd9Sstevel@tonic-gate { 2662*7c478bd9Sstevel@tonic-gate char *q; 2663*7c478bd9Sstevel@tonic-gate 2664*7c478bd9Sstevel@tonic-gate for (p = m->m_execdir; p != NULL; p = q) 2665*7c478bd9Sstevel@tonic-gate { 2666*7c478bd9Sstevel@tonic-gate q = strchr(p, ':'); 2667*7c478bd9Sstevel@tonic-gate if (q != NULL) 2668*7c478bd9Sstevel@tonic-gate *q = '\0'; 2669*7c478bd9Sstevel@tonic-gate expand(p, cbuf, sizeof cbuf, e); 2670*7c478bd9Sstevel@tonic-gate if (q != NULL) 2671*7c478bd9Sstevel@tonic-gate *q++ = ':'; 2672*7c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 2673*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: trydir %s\n", 2674*7c478bd9Sstevel@tonic-gate cbuf); 2675*7c478bd9Sstevel@tonic-gate if (cbuf[0] != '\0' && 2676*7c478bd9Sstevel@tonic-gate chdir(cbuf) >= 0) 2677*7c478bd9Sstevel@tonic-gate break; 2678*7c478bd9Sstevel@tonic-gate } 2679*7c478bd9Sstevel@tonic-gate } 2680*7c478bd9Sstevel@tonic-gate 2681*7c478bd9Sstevel@tonic-gate /* Check safety of program to be run */ 2682*7c478bd9Sstevel@tonic-gate sff = SFF_ROOTOK|SFF_EXECOK; 2683*7c478bd9Sstevel@tonic-gate if (!bitnset(DBS_RUNWRITABLEPROGRAM, 2684*7c478bd9Sstevel@tonic-gate DontBlameSendmail)) 2685*7c478bd9Sstevel@tonic-gate sff |= SFF_NOGWFILES|SFF_NOWWFILES; 2686*7c478bd9Sstevel@tonic-gate if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, 2687*7c478bd9Sstevel@tonic-gate DontBlameSendmail)) 2688*7c478bd9Sstevel@tonic-gate sff |= SFF_NOPATHCHECK; 2689*7c478bd9Sstevel@tonic-gate else 2690*7c478bd9Sstevel@tonic-gate sff |= SFF_SAFEDIRPATH; 2691*7c478bd9Sstevel@tonic-gate ret = safefile(m->m_mailer, getuid(), getgid(), 2692*7c478bd9Sstevel@tonic-gate user, sff, 0, NULL); 2693*7c478bd9Sstevel@tonic-gate if (ret != 0) 2694*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, 2695*7c478bd9Sstevel@tonic-gate "Warning: program %s unsafe: %s", 2696*7c478bd9Sstevel@tonic-gate m->m_mailer, sm_errstring(ret)); 2697*7c478bd9Sstevel@tonic-gate 2698*7c478bd9Sstevel@tonic-gate /* arrange to filter std & diag output of command */ 2699*7c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 2700*7c478bd9Sstevel@tonic-gate if (dup2(rpvect[1], STDOUT_FILENO) < 0) 2701*7c478bd9Sstevel@tonic-gate { 2702*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", 2703*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2704*7c478bd9Sstevel@tonic-gate m->m_name, rpvect[1]); 2705*7c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 2706*7c478bd9Sstevel@tonic-gate } 2707*7c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 2708*7c478bd9Sstevel@tonic-gate 2709*7c478bd9Sstevel@tonic-gate if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) 2710*7c478bd9Sstevel@tonic-gate { 2711*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup stdout for stderr", 2712*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2713*7c478bd9Sstevel@tonic-gate m->m_name); 2714*7c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 2715*7c478bd9Sstevel@tonic-gate } 2716*7c478bd9Sstevel@tonic-gate 2717*7c478bd9Sstevel@tonic-gate /* arrange to get standard input */ 2718*7c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 2719*7c478bd9Sstevel@tonic-gate if (dup2(mpvect[0], STDIN_FILENO) < 0) 2720*7c478bd9Sstevel@tonic-gate { 2721*7c478bd9Sstevel@tonic-gate syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", 2722*7c478bd9Sstevel@tonic-gate shortenstring(e->e_to, MAXSHORTSTR), 2723*7c478bd9Sstevel@tonic-gate m->m_name, mpvect[0]); 2724*7c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 2725*7c478bd9Sstevel@tonic-gate } 2726*7c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 2727*7c478bd9Sstevel@tonic-gate 2728*7c478bd9Sstevel@tonic-gate /* arrange for all the files to be closed */ 2729*7c478bd9Sstevel@tonic-gate sm_close_on_exec(STDERR_FILENO + 1, DtableSize); 2730*7c478bd9Sstevel@tonic-gate 2731*7c478bd9Sstevel@tonic-gate # if !_FFR_USE_SETLOGIN 2732*7c478bd9Sstevel@tonic-gate /* run disconnected from terminal */ 2733*7c478bd9Sstevel@tonic-gate (void) setsid(); 2734*7c478bd9Sstevel@tonic-gate # endif /* !_FFR_USE_SETLOGIN */ 2735*7c478bd9Sstevel@tonic-gate 2736*7c478bd9Sstevel@tonic-gate /* try to execute the mailer */ 2737*7c478bd9Sstevel@tonic-gate (void) execve(m->m_mailer, (ARGV_T) pv, 2738*7c478bd9Sstevel@tonic-gate (ARGV_T) UserEnviron); 2739*7c478bd9Sstevel@tonic-gate save_errno = errno; 2740*7c478bd9Sstevel@tonic-gate syserr("Cannot exec %s", m->m_mailer); 2741*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) || 2742*7c478bd9Sstevel@tonic-gate transienterror(save_errno)) 2743*7c478bd9Sstevel@tonic-gate _exit(EX_OSERR); 2744*7c478bd9Sstevel@tonic-gate _exit(EX_UNAVAILABLE); 2745*7c478bd9Sstevel@tonic-gate } 2746*7c478bd9Sstevel@tonic-gate 2747*7c478bd9Sstevel@tonic-gate /* 2748*7c478bd9Sstevel@tonic-gate ** Set up return value. 2749*7c478bd9Sstevel@tonic-gate */ 2750*7c478bd9Sstevel@tonic-gate 2751*7c478bd9Sstevel@tonic-gate if (mci == NULL) 2752*7c478bd9Sstevel@tonic-gate { 2753*7c478bd9Sstevel@tonic-gate if (clever) 2754*7c478bd9Sstevel@tonic-gate { 2755*7c478bd9Sstevel@tonic-gate /* 2756*7c478bd9Sstevel@tonic-gate ** Allocate from general heap, not 2757*7c478bd9Sstevel@tonic-gate ** envelope rpool, because this mci 2758*7c478bd9Sstevel@tonic-gate ** is going to be cached. 2759*7c478bd9Sstevel@tonic-gate */ 2760*7c478bd9Sstevel@tonic-gate 2761*7c478bd9Sstevel@tonic-gate mci = mci_new(NULL); 2762*7c478bd9Sstevel@tonic-gate } 2763*7c478bd9Sstevel@tonic-gate else 2764*7c478bd9Sstevel@tonic-gate { 2765*7c478bd9Sstevel@tonic-gate /* 2766*7c478bd9Sstevel@tonic-gate ** Prevent a storage leak by allocating 2767*7c478bd9Sstevel@tonic-gate ** this from the envelope rpool. 2768*7c478bd9Sstevel@tonic-gate */ 2769*7c478bd9Sstevel@tonic-gate 2770*7c478bd9Sstevel@tonic-gate mci = mci_new(e->e_rpool); 2771*7c478bd9Sstevel@tonic-gate } 2772*7c478bd9Sstevel@tonic-gate } 2773*7c478bd9Sstevel@tonic-gate mci->mci_mailer = m; 2774*7c478bd9Sstevel@tonic-gate if (clever) 2775*7c478bd9Sstevel@tonic-gate { 2776*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPENING; 2777*7c478bd9Sstevel@tonic-gate mci_cache(mci); 2778*7c478bd9Sstevel@tonic-gate } 2779*7c478bd9Sstevel@tonic-gate else 2780*7c478bd9Sstevel@tonic-gate { 2781*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 2782*7c478bd9Sstevel@tonic-gate } 2783*7c478bd9Sstevel@tonic-gate mci->mci_pid = pid; 2784*7c478bd9Sstevel@tonic-gate (void) close(mpvect[0]); 2785*7c478bd9Sstevel@tonic-gate mci->mci_out = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2786*7c478bd9Sstevel@tonic-gate (void *) &(mpvect[1]), SM_IO_WRONLY_B, 2787*7c478bd9Sstevel@tonic-gate NULL); 2788*7c478bd9Sstevel@tonic-gate if (mci->mci_out == NULL) 2789*7c478bd9Sstevel@tonic-gate { 2790*7c478bd9Sstevel@tonic-gate syserr("deliver: cannot create mailer output channel, fd=%d", 2791*7c478bd9Sstevel@tonic-gate mpvect[1]); 2792*7c478bd9Sstevel@tonic-gate (void) close(mpvect[1]); 2793*7c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 2794*7c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 2795*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2796*7c478bd9Sstevel@tonic-gate goto give_up; 2797*7c478bd9Sstevel@tonic-gate } 2798*7c478bd9Sstevel@tonic-gate 2799*7c478bd9Sstevel@tonic-gate (void) close(rpvect[1]); 2800*7c478bd9Sstevel@tonic-gate mci->mci_in = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 2801*7c478bd9Sstevel@tonic-gate (void *) &(rpvect[0]), SM_IO_RDONLY_B, 2802*7c478bd9Sstevel@tonic-gate NULL); 2803*7c478bd9Sstevel@tonic-gate if (mci->mci_in == NULL) 2804*7c478bd9Sstevel@tonic-gate { 2805*7c478bd9Sstevel@tonic-gate syserr("deliver: cannot create mailer input channel, fd=%d", 2806*7c478bd9Sstevel@tonic-gate mpvect[1]); 2807*7c478bd9Sstevel@tonic-gate (void) close(rpvect[0]); 2808*7c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 2809*7c478bd9Sstevel@tonic-gate mci->mci_out = NULL; 2810*7c478bd9Sstevel@tonic-gate rcode = EX_OSERR; 2811*7c478bd9Sstevel@tonic-gate goto give_up; 2812*7c478bd9Sstevel@tonic-gate } 2813*7c478bd9Sstevel@tonic-gate } 2814*7c478bd9Sstevel@tonic-gate 2815*7c478bd9Sstevel@tonic-gate /* 2816*7c478bd9Sstevel@tonic-gate ** If we are in SMTP opening state, send initial protocol. 2817*7c478bd9Sstevel@tonic-gate */ 2818*7c478bd9Sstevel@tonic-gate 2819*7c478bd9Sstevel@tonic-gate if (bitnset(M_7BITS, m->m_flags) && 2820*7c478bd9Sstevel@tonic-gate (!clever || mci->mci_state == MCIS_OPENING)) 2821*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_7BIT; 2822*7c478bd9Sstevel@tonic-gate if (clever && mci->mci_state != MCIS_CLOSED) 2823*7c478bd9Sstevel@tonic-gate { 2824*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 2825*7c478bd9Sstevel@tonic-gate int dotpos; 2826*7c478bd9Sstevel@tonic-gate char *srvname; 2827*7c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 2828*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 2829*7c478bd9Sstevel@tonic-gate 2830*7c478bd9Sstevel@tonic-gate # if SASL 2831*7c478bd9Sstevel@tonic-gate # define DONE_AUTH(f) bitset(MCIF_AUTHACT, f) 2832*7c478bd9Sstevel@tonic-gate # endif /* SASL */ 2833*7c478bd9Sstevel@tonic-gate # if STARTTLS 2834*7c478bd9Sstevel@tonic-gate # define DONE_STARTTLS(f) bitset(MCIF_TLSACT, f) 2835*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 2836*7c478bd9Sstevel@tonic-gate # define ONLY_HELO(f) bitset(MCIF_ONLY_EHLO, f) 2837*7c478bd9Sstevel@tonic-gate # define SET_HELO(f) f |= MCIF_ONLY_EHLO 2838*7c478bd9Sstevel@tonic-gate # define CLR_HELO(f) f &= ~MCIF_ONLY_EHLO 2839*7c478bd9Sstevel@tonic-gate 2840*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 2841*7c478bd9Sstevel@tonic-gate /* don't use CurHostName, it is changed in many places */ 2842*7c478bd9Sstevel@tonic-gate if (mci->mci_host != NULL) 2843*7c478bd9Sstevel@tonic-gate { 2844*7c478bd9Sstevel@tonic-gate srvname = mci->mci_host; 2845*7c478bd9Sstevel@tonic-gate dotpos = strlen(srvname) - 1; 2846*7c478bd9Sstevel@tonic-gate if (dotpos >= 0) 2847*7c478bd9Sstevel@tonic-gate { 2848*7c478bd9Sstevel@tonic-gate if (srvname[dotpos] == '.') 2849*7c478bd9Sstevel@tonic-gate srvname[dotpos] = '\0'; 2850*7c478bd9Sstevel@tonic-gate else 2851*7c478bd9Sstevel@tonic-gate dotpos = -1; 2852*7c478bd9Sstevel@tonic-gate } 2853*7c478bd9Sstevel@tonic-gate } 2854*7c478bd9Sstevel@tonic-gate else if (mci->mci_mailer != NULL) 2855*7c478bd9Sstevel@tonic-gate { 2856*7c478bd9Sstevel@tonic-gate srvname = mci->mci_mailer->m_name; 2857*7c478bd9Sstevel@tonic-gate dotpos = -1; 2858*7c478bd9Sstevel@tonic-gate } 2859*7c478bd9Sstevel@tonic-gate else 2860*7c478bd9Sstevel@tonic-gate { 2861*7c478bd9Sstevel@tonic-gate srvname = "local"; 2862*7c478bd9Sstevel@tonic-gate dotpos = -1; 2863*7c478bd9Sstevel@tonic-gate } 2864*7c478bd9Sstevel@tonic-gate 2865*7c478bd9Sstevel@tonic-gate /* don't set {server_name} to NULL or "": see getauth() */ 2866*7c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_TEMP, macid("{server_name}"), 2867*7c478bd9Sstevel@tonic-gate srvname); 2868*7c478bd9Sstevel@tonic-gate 2869*7c478bd9Sstevel@tonic-gate /* CurHostAddr is set by makeconnection() and mci_get() */ 2870*7c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 2871*7c478bd9Sstevel@tonic-gate { 2872*7c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_TEMP, 2873*7c478bd9Sstevel@tonic-gate macid("{server_addr}"), 2874*7c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 2875*7c478bd9Sstevel@tonic-gate } 2876*7c478bd9Sstevel@tonic-gate else if (mci->mci_mailer != NULL) 2877*7c478bd9Sstevel@tonic-gate { 2878*7c478bd9Sstevel@tonic-gate /* mailer name is unique, use it as address */ 2879*7c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_PERM, 2880*7c478bd9Sstevel@tonic-gate macid("{server_addr}"), 2881*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name); 2882*7c478bd9Sstevel@tonic-gate } 2883*7c478bd9Sstevel@tonic-gate else 2884*7c478bd9Sstevel@tonic-gate { 2885*7c478bd9Sstevel@tonic-gate /* don't set it to NULL or "": see getauth() */ 2886*7c478bd9Sstevel@tonic-gate macdefine(&mci->mci_macro, A_PERM, 2887*7c478bd9Sstevel@tonic-gate macid("{server_addr}"), "0"); 2888*7c478bd9Sstevel@tonic-gate } 2889*7c478bd9Sstevel@tonic-gate 2890*7c478bd9Sstevel@tonic-gate /* undo change of srvname (mci->mci_host) */ 2891*7c478bd9Sstevel@tonic-gate if (dotpos >= 0) 2892*7c478bd9Sstevel@tonic-gate srvname[dotpos] = '.'; 2893*7c478bd9Sstevel@tonic-gate 2894*7c478bd9Sstevel@tonic-gate reconnect: /* after switching to an encrypted connection */ 2895*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 2896*7c478bd9Sstevel@tonic-gate 2897*7c478bd9Sstevel@tonic-gate /* set the current connection information */ 2898*7c478bd9Sstevel@tonic-gate e->e_mci = mci; 2899*7c478bd9Sstevel@tonic-gate # if SASL 2900*7c478bd9Sstevel@tonic-gate mci->mci_saslcap = NULL; 2901*7c478bd9Sstevel@tonic-gate # endif /* SASL */ 2902*7c478bd9Sstevel@tonic-gate smtpinit(m, mci, e, ONLY_HELO(mci->mci_flags)); 2903*7c478bd9Sstevel@tonic-gate CLR_HELO(mci->mci_flags); 2904*7c478bd9Sstevel@tonic-gate 2905*7c478bd9Sstevel@tonic-gate if (IS_DLVR_RETURN(e)) 2906*7c478bd9Sstevel@tonic-gate { 2907*7c478bd9Sstevel@tonic-gate /* 2908*7c478bd9Sstevel@tonic-gate ** Check whether other side can deliver e-mail 2909*7c478bd9Sstevel@tonic-gate ** fast enough 2910*7c478bd9Sstevel@tonic-gate */ 2911*7c478bd9Sstevel@tonic-gate 2912*7c478bd9Sstevel@tonic-gate if (!bitset(MCIF_DLVR_BY, mci->mci_flags)) 2913*7c478bd9Sstevel@tonic-gate { 2914*7c478bd9Sstevel@tonic-gate e->e_status = "5.4.7"; 2915*7c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 2916*7c478bd9Sstevel@tonic-gate "554 Server does not support Deliver By"); 2917*7c478bd9Sstevel@tonic-gate rcode = EX_UNAVAILABLE; 2918*7c478bd9Sstevel@tonic-gate goto give_up; 2919*7c478bd9Sstevel@tonic-gate } 2920*7c478bd9Sstevel@tonic-gate if (e->e_deliver_by > 0 && 2921*7c478bd9Sstevel@tonic-gate e->e_deliver_by - (curtime() - e->e_ctime) < 2922*7c478bd9Sstevel@tonic-gate mci->mci_min_by) 2923*7c478bd9Sstevel@tonic-gate { 2924*7c478bd9Sstevel@tonic-gate e->e_status = "5.4.7"; 2925*7c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 2926*7c478bd9Sstevel@tonic-gate "554 Message can't be delivered in time; %ld < %ld", 2927*7c478bd9Sstevel@tonic-gate e->e_deliver_by - (curtime() - e->e_ctime), 2928*7c478bd9Sstevel@tonic-gate mci->mci_min_by); 2929*7c478bd9Sstevel@tonic-gate rcode = EX_UNAVAILABLE; 2930*7c478bd9Sstevel@tonic-gate goto give_up; 2931*7c478bd9Sstevel@tonic-gate } 2932*7c478bd9Sstevel@tonic-gate } 2933*7c478bd9Sstevel@tonic-gate 2934*7c478bd9Sstevel@tonic-gate # if STARTTLS 2935*7c478bd9Sstevel@tonic-gate /* first TLS then AUTH to provide a security layer */ 2936*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED && 2937*7c478bd9Sstevel@tonic-gate !DONE_STARTTLS(mci->mci_flags)) 2938*7c478bd9Sstevel@tonic-gate { 2939*7c478bd9Sstevel@tonic-gate int olderrors; 2940*7c478bd9Sstevel@tonic-gate bool usetls; 2941*7c478bd9Sstevel@tonic-gate bool saveQuickAbort = QuickAbort; 2942*7c478bd9Sstevel@tonic-gate bool saveSuprErrs = SuprErrs; 2943*7c478bd9Sstevel@tonic-gate char *host = NULL; 2944*7c478bd9Sstevel@tonic-gate 2945*7c478bd9Sstevel@tonic-gate rcode = EX_OK; 2946*7c478bd9Sstevel@tonic-gate usetls = bitset(MCIF_TLS, mci->mci_flags); 2947*7c478bd9Sstevel@tonic-gate if (usetls) 2948*7c478bd9Sstevel@tonic-gate usetls = !iscltflgset(e, D_NOTLS); 2949*7c478bd9Sstevel@tonic-gate 2950*7c478bd9Sstevel@tonic-gate if (usetls) 2951*7c478bd9Sstevel@tonic-gate { 2952*7c478bd9Sstevel@tonic-gate host = macvalue(macid("{server_name}"), e); 2953*7c478bd9Sstevel@tonic-gate olderrors = Errors; 2954*7c478bd9Sstevel@tonic-gate QuickAbort = false; 2955*7c478bd9Sstevel@tonic-gate SuprErrs = true; 2956*7c478bd9Sstevel@tonic-gate if (rscheck("try_tls", host, NULL, e, 2957*7c478bd9Sstevel@tonic-gate RSF_RMCOMM, 7, host, NOQID) != EX_OK 2958*7c478bd9Sstevel@tonic-gate || Errors > olderrors) 2959*7c478bd9Sstevel@tonic-gate usetls = false; 2960*7c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 2961*7c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 2962*7c478bd9Sstevel@tonic-gate } 2963*7c478bd9Sstevel@tonic-gate 2964*7c478bd9Sstevel@tonic-gate if (usetls) 2965*7c478bd9Sstevel@tonic-gate { 2966*7c478bd9Sstevel@tonic-gate if ((rcode = starttls(m, mci, e)) == EX_OK) 2967*7c478bd9Sstevel@tonic-gate { 2968*7c478bd9Sstevel@tonic-gate /* start again without STARTTLS */ 2969*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_TLSACT; 2970*7c478bd9Sstevel@tonic-gate } 2971*7c478bd9Sstevel@tonic-gate else 2972*7c478bd9Sstevel@tonic-gate { 2973*7c478bd9Sstevel@tonic-gate char *s; 2974*7c478bd9Sstevel@tonic-gate 2975*7c478bd9Sstevel@tonic-gate /* 2976*7c478bd9Sstevel@tonic-gate ** TLS negotation failed, what to do? 2977*7c478bd9Sstevel@tonic-gate ** fall back to unencrypted connection 2978*7c478bd9Sstevel@tonic-gate ** or abort? How to decide? 2979*7c478bd9Sstevel@tonic-gate ** set a macro and call a ruleset. 2980*7c478bd9Sstevel@tonic-gate */ 2981*7c478bd9Sstevel@tonic-gate 2982*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLS; 2983*7c478bd9Sstevel@tonic-gate switch (rcode) 2984*7c478bd9Sstevel@tonic-gate { 2985*7c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 2986*7c478bd9Sstevel@tonic-gate s = "TEMP"; 2987*7c478bd9Sstevel@tonic-gate break; 2988*7c478bd9Sstevel@tonic-gate case EX_USAGE: 2989*7c478bd9Sstevel@tonic-gate s = "USAGE"; 2990*7c478bd9Sstevel@tonic-gate break; 2991*7c478bd9Sstevel@tonic-gate case EX_PROTOCOL: 2992*7c478bd9Sstevel@tonic-gate s = "PROTOCOL"; 2993*7c478bd9Sstevel@tonic-gate break; 2994*7c478bd9Sstevel@tonic-gate case EX_SOFTWARE: 2995*7c478bd9Sstevel@tonic-gate s = "SOFTWARE"; 2996*7c478bd9Sstevel@tonic-gate break; 2997*7c478bd9Sstevel@tonic-gate 2998*7c478bd9Sstevel@tonic-gate /* everything else is a failure */ 2999*7c478bd9Sstevel@tonic-gate default: 3000*7c478bd9Sstevel@tonic-gate s = "FAILURE"; 3001*7c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 3002*7c478bd9Sstevel@tonic-gate } 3003*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 3004*7c478bd9Sstevel@tonic-gate macid("{verify}"), s); 3005*7c478bd9Sstevel@tonic-gate } 3006*7c478bd9Sstevel@tonic-gate } 3007*7c478bd9Sstevel@tonic-gate else 3008*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 3009*7c478bd9Sstevel@tonic-gate macid("{verify}"), "NONE"); 3010*7c478bd9Sstevel@tonic-gate olderrors = Errors; 3011*7c478bd9Sstevel@tonic-gate QuickAbort = false; 3012*7c478bd9Sstevel@tonic-gate SuprErrs = true; 3013*7c478bd9Sstevel@tonic-gate 3014*7c478bd9Sstevel@tonic-gate /* 3015*7c478bd9Sstevel@tonic-gate ** rcode == EX_SOFTWARE is special: 3016*7c478bd9Sstevel@tonic-gate ** the TLS negotation failed 3017*7c478bd9Sstevel@tonic-gate ** we have to drop the connection no matter what 3018*7c478bd9Sstevel@tonic-gate ** However, we call tls_server to give it the chance 3019*7c478bd9Sstevel@tonic-gate ** to log the problem and return an appropriate 3020*7c478bd9Sstevel@tonic-gate ** error code. 3021*7c478bd9Sstevel@tonic-gate */ 3022*7c478bd9Sstevel@tonic-gate 3023*7c478bd9Sstevel@tonic-gate if (rscheck("tls_server", 3024*7c478bd9Sstevel@tonic-gate macvalue(macid("{verify}"), e), 3025*7c478bd9Sstevel@tonic-gate NULL, e, RSF_RMCOMM|RSF_COUNT, 5, 3026*7c478bd9Sstevel@tonic-gate host, NOQID) != EX_OK || 3027*7c478bd9Sstevel@tonic-gate Errors > olderrors || 3028*7c478bd9Sstevel@tonic-gate rcode == EX_SOFTWARE) 3029*7c478bd9Sstevel@tonic-gate { 3030*7c478bd9Sstevel@tonic-gate char enhsc[ENHSCLEN]; 3031*7c478bd9Sstevel@tonic-gate extern char MsgBuf[]; 3032*7c478bd9Sstevel@tonic-gate 3033*7c478bd9Sstevel@tonic-gate if (ISSMTPCODE(MsgBuf) && 3034*7c478bd9Sstevel@tonic-gate extenhsc(MsgBuf + 4, ' ', enhsc) > 0) 3035*7c478bd9Sstevel@tonic-gate { 3036*7c478bd9Sstevel@tonic-gate p = sm_rpool_strdup_x(e->e_rpool, 3037*7c478bd9Sstevel@tonic-gate MsgBuf); 3038*7c478bd9Sstevel@tonic-gate } 3039*7c478bd9Sstevel@tonic-gate else 3040*7c478bd9Sstevel@tonic-gate { 3041*7c478bd9Sstevel@tonic-gate p = "403 4.7.0 server not authenticated."; 3042*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(enhsc, "4.7.0", 3043*7c478bd9Sstevel@tonic-gate sizeof enhsc); 3044*7c478bd9Sstevel@tonic-gate } 3045*7c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 3046*7c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 3047*7c478bd9Sstevel@tonic-gate 3048*7c478bd9Sstevel@tonic-gate if (rcode == EX_SOFTWARE) 3049*7c478bd9Sstevel@tonic-gate { 3050*7c478bd9Sstevel@tonic-gate /* drop the connection */ 3051*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_QUITING; 3052*7c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL) 3053*7c478bd9Sstevel@tonic-gate { 3054*7c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_in, 3055*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT); 3056*7c478bd9Sstevel@tonic-gate mci->mci_in = NULL; 3057*7c478bd9Sstevel@tonic-gate } 3058*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLSACT; 3059*7c478bd9Sstevel@tonic-gate (void) endmailer(mci, e, pv); 3060*7c478bd9Sstevel@tonic-gate } 3061*7c478bd9Sstevel@tonic-gate else 3062*7c478bd9Sstevel@tonic-gate { 3063*7c478bd9Sstevel@tonic-gate /* abort transfer */ 3064*7c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 3065*7c478bd9Sstevel@tonic-gate } 3066*7c478bd9Sstevel@tonic-gate 3067*7c478bd9Sstevel@tonic-gate /* avoid bogus error msg */ 3068*7c478bd9Sstevel@tonic-gate mci->mci_errno = 0; 3069*7c478bd9Sstevel@tonic-gate 3070*7c478bd9Sstevel@tonic-gate /* temp or permanent failure? */ 3071*7c478bd9Sstevel@tonic-gate rcode = (*p == '4') ? EX_TEMPFAIL 3072*7c478bd9Sstevel@tonic-gate : EX_UNAVAILABLE; 3073*7c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, enhsc, p); 3074*7c478bd9Sstevel@tonic-gate 3075*7c478bd9Sstevel@tonic-gate /* 3076*7c478bd9Sstevel@tonic-gate ** hack to get the error message into 3077*7c478bd9Sstevel@tonic-gate ** the envelope (done in giveresponse()) 3078*7c478bd9Sstevel@tonic-gate */ 3079*7c478bd9Sstevel@tonic-gate 3080*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(SmtpError, p, 3081*7c478bd9Sstevel@tonic-gate sizeof SmtpError); 3082*7c478bd9Sstevel@tonic-gate } 3083*7c478bd9Sstevel@tonic-gate QuickAbort = saveQuickAbort; 3084*7c478bd9Sstevel@tonic-gate SuprErrs = saveSuprErrs; 3085*7c478bd9Sstevel@tonic-gate if (DONE_STARTTLS(mci->mci_flags) && 3086*7c478bd9Sstevel@tonic-gate mci->mci_state != MCIS_CLOSED) 3087*7c478bd9Sstevel@tonic-gate { 3088*7c478bd9Sstevel@tonic-gate SET_HELO(mci->mci_flags); 3089*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_EXTENS; 3090*7c478bd9Sstevel@tonic-gate goto reconnect; 3091*7c478bd9Sstevel@tonic-gate } 3092*7c478bd9Sstevel@tonic-gate } 3093*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 3094*7c478bd9Sstevel@tonic-gate # if SASL 3095*7c478bd9Sstevel@tonic-gate /* if other server supports authentication let's authenticate */ 3096*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_CLOSED && 3097*7c478bd9Sstevel@tonic-gate mci->mci_saslcap != NULL && 3098*7c478bd9Sstevel@tonic-gate !DONE_AUTH(mci->mci_flags) && !iscltflgset(e, D_NOAUTH)) 3099*7c478bd9Sstevel@tonic-gate { 3100*7c478bd9Sstevel@tonic-gate /* Should we require some minimum authentication? */ 3101*7c478bd9Sstevel@tonic-gate if ((ret = smtpauth(m, mci, e)) == EX_OK) 3102*7c478bd9Sstevel@tonic-gate { 3103*7c478bd9Sstevel@tonic-gate int result; 3104*7c478bd9Sstevel@tonic-gate sasl_ssf_t *ssf = NULL; 3105*7c478bd9Sstevel@tonic-gate 3106*7c478bd9Sstevel@tonic-gate /* Get security strength (features) */ 3107*7c478bd9Sstevel@tonic-gate result = sasl_getprop(mci->mci_conn, SASL_SSF, 3108*7c478bd9Sstevel@tonic-gate # if SASL >= 20000 3109*7c478bd9Sstevel@tonic-gate (const void **) &ssf); 3110*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */ 3111*7c478bd9Sstevel@tonic-gate (void **) &ssf); 3112*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */ 3113*7c478bd9Sstevel@tonic-gate 3114*7c478bd9Sstevel@tonic-gate /* XXX authid? */ 3115*7c478bd9Sstevel@tonic-gate if (LogLevel > 9) 3116*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, 3117*7c478bd9Sstevel@tonic-gate "AUTH=client, relay=%.100s, mech=%.16s, bits=%d", 3118*7c478bd9Sstevel@tonic-gate mci->mci_host, 3119*7c478bd9Sstevel@tonic-gate macvalue(macid("{auth_type}"), e), 3120*7c478bd9Sstevel@tonic-gate result == SASL_OK ? *ssf : 0); 3121*7c478bd9Sstevel@tonic-gate 3122*7c478bd9Sstevel@tonic-gate /* 3123*7c478bd9Sstevel@tonic-gate ** Only switch to encrypted connection 3124*7c478bd9Sstevel@tonic-gate ** if a security layer has been negotiated 3125*7c478bd9Sstevel@tonic-gate */ 3126*7c478bd9Sstevel@tonic-gate 3127*7c478bd9Sstevel@tonic-gate if (result == SASL_OK && *ssf > 0) 3128*7c478bd9Sstevel@tonic-gate { 3129*7c478bd9Sstevel@tonic-gate /* 3130*7c478bd9Sstevel@tonic-gate ** Convert I/O layer to use SASL. 3131*7c478bd9Sstevel@tonic-gate ** If the call fails, the connection 3132*7c478bd9Sstevel@tonic-gate ** is aborted. 3133*7c478bd9Sstevel@tonic-gate */ 3134*7c478bd9Sstevel@tonic-gate 3135*7c478bd9Sstevel@tonic-gate if (sfdcsasl(&mci->mci_in, 3136*7c478bd9Sstevel@tonic-gate &mci->mci_out, 3137*7c478bd9Sstevel@tonic-gate mci->mci_conn) == 0) 3138*7c478bd9Sstevel@tonic-gate { 3139*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_EXTENS; 3140*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_AUTHACT| 3141*7c478bd9Sstevel@tonic-gate MCIF_ONLY_EHLO; 3142*7c478bd9Sstevel@tonic-gate goto reconnect; 3143*7c478bd9Sstevel@tonic-gate } 3144*7c478bd9Sstevel@tonic-gate syserr("AUTH TLS switch failed in client"); 3145*7c478bd9Sstevel@tonic-gate } 3146*7c478bd9Sstevel@tonic-gate /* else? XXX */ 3147*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_AUTHACT; 3148*7c478bd9Sstevel@tonic-gate 3149*7c478bd9Sstevel@tonic-gate } 3150*7c478bd9Sstevel@tonic-gate else if (ret == EX_TEMPFAIL) 3151*7c478bd9Sstevel@tonic-gate { 3152*7c478bd9Sstevel@tonic-gate if (LogLevel > 8) 3153*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 3154*7c478bd9Sstevel@tonic-gate "AUTH=client, relay=%.100s, temporary failure, connection abort", 3155*7c478bd9Sstevel@tonic-gate mci->mci_host); 3156*7c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 3157*7c478bd9Sstevel@tonic-gate 3158*7c478bd9Sstevel@tonic-gate /* avoid bogus error msg */ 3159*7c478bd9Sstevel@tonic-gate mci->mci_errno = 0; 3160*7c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 3161*7c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, "4.3.0", p); 3162*7c478bd9Sstevel@tonic-gate 3163*7c478bd9Sstevel@tonic-gate /* 3164*7c478bd9Sstevel@tonic-gate ** hack to get the error message into 3165*7c478bd9Sstevel@tonic-gate ** the envelope (done in giveresponse()) 3166*7c478bd9Sstevel@tonic-gate */ 3167*7c478bd9Sstevel@tonic-gate 3168*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(SmtpError, 3169*7c478bd9Sstevel@tonic-gate "Temporary AUTH failure", 3170*7c478bd9Sstevel@tonic-gate sizeof SmtpError); 3171*7c478bd9Sstevel@tonic-gate } 3172*7c478bd9Sstevel@tonic-gate } 3173*7c478bd9Sstevel@tonic-gate # endif /* SASL */ 3174*7c478bd9Sstevel@tonic-gate } 3175*7c478bd9Sstevel@tonic-gate 3176*7c478bd9Sstevel@tonic-gate 3177*7c478bd9Sstevel@tonic-gate do_transfer: 3178*7c478bd9Sstevel@tonic-gate /* clear out per-message flags from connection structure */ 3179*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 3180*7c478bd9Sstevel@tonic-gate 3181*7c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 3182*7c478bd9Sstevel@tonic-gate !bitset(EF_DONT_MIME, e->e_flags) && 3183*7c478bd9Sstevel@tonic-gate bitnset(M_7BITS, m->m_flags)) 3184*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_CVT8TO7; 3185*7c478bd9Sstevel@tonic-gate 3186*7c478bd9Sstevel@tonic-gate #if MIME7TO8 3187*7c478bd9Sstevel@tonic-gate if (bitnset(M_MAKE8BIT, m->m_flags) && 3188*7c478bd9Sstevel@tonic-gate !bitset(MCIF_7BIT, mci->mci_flags) && 3189*7c478bd9Sstevel@tonic-gate (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 3190*7c478bd9Sstevel@tonic-gate (sm_strcasecmp(p, "quoted-printable") == 0 || 3191*7c478bd9Sstevel@tonic-gate sm_strcasecmp(p, "base64") == 0) && 3192*7c478bd9Sstevel@tonic-gate (p = hvalue("Content-Type", e->e_header)) != NULL) 3193*7c478bd9Sstevel@tonic-gate { 3194*7c478bd9Sstevel@tonic-gate /* may want to convert 7 -> 8 */ 3195*7c478bd9Sstevel@tonic-gate /* XXX should really parse it here -- and use a class XXX */ 3196*7c478bd9Sstevel@tonic-gate if (sm_strncasecmp(p, "text/plain", 10) == 0 && 3197*7c478bd9Sstevel@tonic-gate (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 3198*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_CVT7TO8; 3199*7c478bd9Sstevel@tonic-gate } 3200*7c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */ 3201*7c478bd9Sstevel@tonic-gate 3202*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 3203*7c478bd9Sstevel@tonic-gate { 3204*7c478bd9Sstevel@tonic-gate sm_dprintf("openmailer: "); 3205*7c478bd9Sstevel@tonic-gate mci_dump(sm_debug_file(), mci, false); 3206*7c478bd9Sstevel@tonic-gate } 3207*7c478bd9Sstevel@tonic-gate 3208*7c478bd9Sstevel@tonic-gate #if _FFR_CLIENT_SIZE 3209*7c478bd9Sstevel@tonic-gate /* 3210*7c478bd9Sstevel@tonic-gate ** See if we know the maximum size and 3211*7c478bd9Sstevel@tonic-gate ** abort if the message is too big. 3212*7c478bd9Sstevel@tonic-gate ** 3213*7c478bd9Sstevel@tonic-gate ** NOTE: _FFR_CLIENT_SIZE is untested. 3214*7c478bd9Sstevel@tonic-gate */ 3215*7c478bd9Sstevel@tonic-gate 3216*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_SIZE, mci->mci_flags) && 3217*7c478bd9Sstevel@tonic-gate mci->mci_maxsize > 0 && 3218*7c478bd9Sstevel@tonic-gate e->e_msgsize > mci->mci_maxsize) 3219*7c478bd9Sstevel@tonic-gate { 3220*7c478bd9Sstevel@tonic-gate e->e_flags |= EF_NO_BODY_RETN; 3221*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags)) 3222*7c478bd9Sstevel@tonic-gate e->e_status = "5.2.3"; 3223*7c478bd9Sstevel@tonic-gate else 3224*7c478bd9Sstevel@tonic-gate e->e_status = "5.3.4"; 3225*7c478bd9Sstevel@tonic-gate 3226*7c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 3227*7c478bd9Sstevel@tonic-gate "552 Message is too large; %ld bytes max", 3228*7c478bd9Sstevel@tonic-gate mci->mci_maxsize); 3229*7c478bd9Sstevel@tonic-gate rcode = EX_DATAERR; 3230*7c478bd9Sstevel@tonic-gate 3231*7c478bd9Sstevel@tonic-gate /* Need an e_message for error */ 3232*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(SmtpError, sizeof SmtpError, 3233*7c478bd9Sstevel@tonic-gate "Message is too large; %ld bytes max", 3234*7c478bd9Sstevel@tonic-gate mci->mci_maxsize); 3235*7c478bd9Sstevel@tonic-gate goto give_up; 3236*7c478bd9Sstevel@tonic-gate } 3237*7c478bd9Sstevel@tonic-gate #endif /* _FFR_CLIENT_SIZE */ 3238*7c478bd9Sstevel@tonic-gate 3239*7c478bd9Sstevel@tonic-gate if (mci->mci_state != MCIS_OPEN) 3240*7c478bd9Sstevel@tonic-gate { 3241*7c478bd9Sstevel@tonic-gate /* couldn't open the mailer */ 3242*7c478bd9Sstevel@tonic-gate rcode = mci->mci_exitstat; 3243*7c478bd9Sstevel@tonic-gate errno = mci->mci_errno; 3244*7c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(mci->mci_herrno); 3245*7c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 3246*7c478bd9Sstevel@tonic-gate { 3247*7c478bd9Sstevel@tonic-gate /* shouldn't happen */ 3248*7c478bd9Sstevel@tonic-gate syserr("554 5.3.5 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", 3249*7c478bd9Sstevel@tonic-gate (unsigned long) mci, rcode, errno, 3250*7c478bd9Sstevel@tonic-gate mci->mci_state, firstsig); 3251*7c478bd9Sstevel@tonic-gate mci_dump_all(smioout, true); 3252*7c478bd9Sstevel@tonic-gate rcode = EX_SOFTWARE; 3253*7c478bd9Sstevel@tonic-gate } 3254*7c478bd9Sstevel@tonic-gate else if (nummxhosts > hostnum) 3255*7c478bd9Sstevel@tonic-gate { 3256*7c478bd9Sstevel@tonic-gate /* try next MX site */ 3257*7c478bd9Sstevel@tonic-gate goto tryhost; 3258*7c478bd9Sstevel@tonic-gate } 3259*7c478bd9Sstevel@tonic-gate } 3260*7c478bd9Sstevel@tonic-gate else if (!clever) 3261*7c478bd9Sstevel@tonic-gate { 3262*7c478bd9Sstevel@tonic-gate /* 3263*7c478bd9Sstevel@tonic-gate ** Format and send message. 3264*7c478bd9Sstevel@tonic-gate */ 3265*7c478bd9Sstevel@tonic-gate 3266*7c478bd9Sstevel@tonic-gate putfromline(mci, e); 3267*7c478bd9Sstevel@tonic-gate (*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER); 3268*7c478bd9Sstevel@tonic-gate (*e->e_putbody)(mci, e, NULL); 3269*7c478bd9Sstevel@tonic-gate 3270*7c478bd9Sstevel@tonic-gate /* get the exit status */ 3271*7c478bd9Sstevel@tonic-gate rcode = endmailer(mci, e, pv); 3272*7c478bd9Sstevel@tonic-gate if (rcode == EX_TEMPFAIL && SmtpError[0] == '\0') 3273*7c478bd9Sstevel@tonic-gate { 3274*7c478bd9Sstevel@tonic-gate /* 3275*7c478bd9Sstevel@tonic-gate ** Need an e_message for mailq display. 3276*7c478bd9Sstevel@tonic-gate ** We set SmtpError as 3277*7c478bd9Sstevel@tonic-gate */ 3278*7c478bd9Sstevel@tonic-gate 3279*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(SmtpError, sizeof SmtpError, 3280*7c478bd9Sstevel@tonic-gate "%s mailer (%s) exited with EX_TEMPFAIL", 3281*7c478bd9Sstevel@tonic-gate m->m_name, m->m_mailer); 3282*7c478bd9Sstevel@tonic-gate } 3283*7c478bd9Sstevel@tonic-gate } 3284*7c478bd9Sstevel@tonic-gate else 3285*7c478bd9Sstevel@tonic-gate { 3286*7c478bd9Sstevel@tonic-gate /* 3287*7c478bd9Sstevel@tonic-gate ** Send the MAIL FROM: protocol 3288*7c478bd9Sstevel@tonic-gate */ 3289*7c478bd9Sstevel@tonic-gate 3290*7c478bd9Sstevel@tonic-gate /* XXX this isn't pipelined... */ 3291*7c478bd9Sstevel@tonic-gate rcode = smtpmailfrom(m, mci, e); 3292*7c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 3293*7c478bd9Sstevel@tonic-gate { 3294*7c478bd9Sstevel@tonic-gate register int i; 3295*7c478bd9Sstevel@tonic-gate # if PIPELINING 3296*7c478bd9Sstevel@tonic-gate ADDRESS *volatile pchain; 3297*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 3298*7c478bd9Sstevel@tonic-gate 3299*7c478bd9Sstevel@tonic-gate /* send the recipient list */ 3300*7c478bd9Sstevel@tonic-gate tobuf[0] = '\0'; 3301*7c478bd9Sstevel@tonic-gate mci->mci_retryrcpt = false; 3302*7c478bd9Sstevel@tonic-gate mci->mci_tolist = tobuf; 3303*7c478bd9Sstevel@tonic-gate # if PIPELINING 3304*7c478bd9Sstevel@tonic-gate pchain = NULL; 3305*7c478bd9Sstevel@tonic-gate mci->mci_nextaddr = NULL; 3306*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 3307*7c478bd9Sstevel@tonic-gate 3308*7c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 3309*7c478bd9Sstevel@tonic-gate { 3310*7c478bd9Sstevel@tonic-gate if (!QS_IS_UNMARKED(to->q_state)) 3311*7c478bd9Sstevel@tonic-gate continue; 3312*7c478bd9Sstevel@tonic-gate 3313*7c478bd9Sstevel@tonic-gate /* mark recipient state as "ok so far" */ 3314*7c478bd9Sstevel@tonic-gate to->q_state = QS_OK; 3315*7c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 3316*7c478bd9Sstevel@tonic-gate # if STARTTLS 3317*7c478bd9Sstevel@tonic-gate i = rscheck("tls_rcpt", to->q_user, NULL, e, 3318*7c478bd9Sstevel@tonic-gate RSF_RMCOMM|RSF_COUNT, 3, 3319*7c478bd9Sstevel@tonic-gate mci->mci_host, e->e_id); 3320*7c478bd9Sstevel@tonic-gate if (i != EX_OK) 3321*7c478bd9Sstevel@tonic-gate { 3322*7c478bd9Sstevel@tonic-gate markfailure(e, to, mci, i, false); 3323*7c478bd9Sstevel@tonic-gate giveresponse(i, to->q_status, m, mci, 3324*7c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 3325*7c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 3326*7c478bd9Sstevel@tonic-gate { 3327*7c478bd9Sstevel@tonic-gate mci->mci_retryrcpt = true; 3328*7c478bd9Sstevel@tonic-gate to->q_state = QS_RETRY; 3329*7c478bd9Sstevel@tonic-gate } 3330*7c478bd9Sstevel@tonic-gate continue; 3331*7c478bd9Sstevel@tonic-gate } 3332*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 3333*7c478bd9Sstevel@tonic-gate 3334*7c478bd9Sstevel@tonic-gate i = smtprcpt(to, m, mci, e, ctladdr, xstart); 3335*7c478bd9Sstevel@tonic-gate # if PIPELINING 3336*7c478bd9Sstevel@tonic-gate if (i == EX_OK && 3337*7c478bd9Sstevel@tonic-gate bitset(MCIF_PIPELINED, mci->mci_flags)) 3338*7c478bd9Sstevel@tonic-gate { 3339*7c478bd9Sstevel@tonic-gate /* 3340*7c478bd9Sstevel@tonic-gate ** Add new element to list of 3341*7c478bd9Sstevel@tonic-gate ** recipients for pipelining. 3342*7c478bd9Sstevel@tonic-gate */ 3343*7c478bd9Sstevel@tonic-gate 3344*7c478bd9Sstevel@tonic-gate to->q_pchain = NULL; 3345*7c478bd9Sstevel@tonic-gate if (mci->mci_nextaddr == NULL) 3346*7c478bd9Sstevel@tonic-gate mci->mci_nextaddr = to; 3347*7c478bd9Sstevel@tonic-gate if (pchain == NULL) 3348*7c478bd9Sstevel@tonic-gate pchain = to; 3349*7c478bd9Sstevel@tonic-gate else 3350*7c478bd9Sstevel@tonic-gate { 3351*7c478bd9Sstevel@tonic-gate pchain->q_pchain = to; 3352*7c478bd9Sstevel@tonic-gate pchain = pchain->q_pchain; 3353*7c478bd9Sstevel@tonic-gate } 3354*7c478bd9Sstevel@tonic-gate } 3355*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 3356*7c478bd9Sstevel@tonic-gate if (i != EX_OK) 3357*7c478bd9Sstevel@tonic-gate { 3358*7c478bd9Sstevel@tonic-gate markfailure(e, to, mci, i, false); 3359*7c478bd9Sstevel@tonic-gate giveresponse(i, to->q_status, m, mci, 3360*7c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 3361*7c478bd9Sstevel@tonic-gate if (i == EX_TEMPFAIL) 3362*7c478bd9Sstevel@tonic-gate to->q_state = QS_RETRY; 3363*7c478bd9Sstevel@tonic-gate } 3364*7c478bd9Sstevel@tonic-gate } 3365*7c478bd9Sstevel@tonic-gate 3366*7c478bd9Sstevel@tonic-gate /* No recipients in list and no missing responses? */ 3367*7c478bd9Sstevel@tonic-gate if (tobuf[0] == '\0' 3368*7c478bd9Sstevel@tonic-gate # if PIPELINING 3369*7c478bd9Sstevel@tonic-gate && mci->mci_nextaddr == NULL 3370*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */ 3371*7c478bd9Sstevel@tonic-gate ) 3372*7c478bd9Sstevel@tonic-gate { 3373*7c478bd9Sstevel@tonic-gate rcode = EX_OK; 3374*7c478bd9Sstevel@tonic-gate e->e_to = NULL; 3375*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_CACHED, mci->mci_flags)) 3376*7c478bd9Sstevel@tonic-gate smtprset(m, mci, e); 3377*7c478bd9Sstevel@tonic-gate } 3378*7c478bd9Sstevel@tonic-gate else 3379*7c478bd9Sstevel@tonic-gate { 3380*7c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 3381*7c478bd9Sstevel@tonic-gate rcode = smtpdata(m, mci, e, ctladdr, xstart); 3382*7c478bd9Sstevel@tonic-gate } 3383*7c478bd9Sstevel@tonic-gate } 3384*7c478bd9Sstevel@tonic-gate if (rcode == EX_TEMPFAIL && nummxhosts > hostnum) 3385*7c478bd9Sstevel@tonic-gate { 3386*7c478bd9Sstevel@tonic-gate /* try next MX site */ 3387*7c478bd9Sstevel@tonic-gate goto tryhost; 3388*7c478bd9Sstevel@tonic-gate } 3389*7c478bd9Sstevel@tonic-gate } 3390*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 3391*7c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 3392*7c478bd9Sstevel@tonic-gate _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ 3393*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 3394*7c478bd9Sstevel@tonic-gate 3395*7c478bd9Sstevel@tonic-gate if (tTd(62, 1)) 3396*7c478bd9Sstevel@tonic-gate checkfds("after delivery"); 3397*7c478bd9Sstevel@tonic-gate 3398*7c478bd9Sstevel@tonic-gate /* 3399*7c478bd9Sstevel@tonic-gate ** Do final status disposal. 3400*7c478bd9Sstevel@tonic-gate ** We check for something in tobuf for the SMTP case. 3401*7c478bd9Sstevel@tonic-gate ** If we got a temporary failure, arrange to queue the 3402*7c478bd9Sstevel@tonic-gate ** addressees. 3403*7c478bd9Sstevel@tonic-gate */ 3404*7c478bd9Sstevel@tonic-gate 3405*7c478bd9Sstevel@tonic-gate give_up: 3406*7c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 3407*7c478bd9Sstevel@tonic-gate { 3408*7c478bd9Sstevel@tonic-gate lmtp_rcode = rcode; 3409*7c478bd9Sstevel@tonic-gate tobuf[0] = '\0'; 3410*7c478bd9Sstevel@tonic-gate anyok = false; 3411*7c478bd9Sstevel@tonic-gate strsize = 0; 3412*7c478bd9Sstevel@tonic-gate } 3413*7c478bd9Sstevel@tonic-gate else 3414*7c478bd9Sstevel@tonic-gate anyok = rcode == EX_OK; 3415*7c478bd9Sstevel@tonic-gate 3416*7c478bd9Sstevel@tonic-gate for (to = tochain; to != NULL; to = to->q_tchain) 3417*7c478bd9Sstevel@tonic-gate { 3418*7c478bd9Sstevel@tonic-gate /* see if address already marked */ 3419*7c478bd9Sstevel@tonic-gate if (!QS_IS_OK(to->q_state)) 3420*7c478bd9Sstevel@tonic-gate continue; 3421*7c478bd9Sstevel@tonic-gate 3422*7c478bd9Sstevel@tonic-gate /* if running LMTP, get the status for each address */ 3423*7c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 3424*7c478bd9Sstevel@tonic-gate { 3425*7c478bd9Sstevel@tonic-gate if (lmtp_rcode == EX_OK) 3426*7c478bd9Sstevel@tonic-gate rcode = smtpgetstat(m, mci, e); 3427*7c478bd9Sstevel@tonic-gate if (rcode == EX_OK) 3428*7c478bd9Sstevel@tonic-gate { 3429*7c478bd9Sstevel@tonic-gate strsize += sm_strlcat2(tobuf + strsize, ",", 3430*7c478bd9Sstevel@tonic-gate to->q_paddr, 3431*7c478bd9Sstevel@tonic-gate tobufsize - strsize); 3432*7c478bd9Sstevel@tonic-gate SM_ASSERT(strsize < tobufsize); 3433*7c478bd9Sstevel@tonic-gate anyok = true; 3434*7c478bd9Sstevel@tonic-gate } 3435*7c478bd9Sstevel@tonic-gate else 3436*7c478bd9Sstevel@tonic-gate { 3437*7c478bd9Sstevel@tonic-gate e->e_to = to->q_paddr; 3438*7c478bd9Sstevel@tonic-gate markfailure(e, to, mci, rcode, true); 3439*7c478bd9Sstevel@tonic-gate giveresponse(rcode, to->q_status, m, mci, 3440*7c478bd9Sstevel@tonic-gate ctladdr, xstart, e, to); 3441*7c478bd9Sstevel@tonic-gate e->e_to = tobuf + 1; 3442*7c478bd9Sstevel@tonic-gate continue; 3443*7c478bd9Sstevel@tonic-gate } 3444*7c478bd9Sstevel@tonic-gate } 3445*7c478bd9Sstevel@tonic-gate else 3446*7c478bd9Sstevel@tonic-gate { 3447*7c478bd9Sstevel@tonic-gate /* mark bad addresses */ 3448*7c478bd9Sstevel@tonic-gate if (rcode != EX_OK) 3449*7c478bd9Sstevel@tonic-gate { 3450*7c478bd9Sstevel@tonic-gate if (goodmxfound && rcode == EX_NOHOST) 3451*7c478bd9Sstevel@tonic-gate rcode = EX_TEMPFAIL; 3452*7c478bd9Sstevel@tonic-gate markfailure(e, to, mci, rcode, true); 3453*7c478bd9Sstevel@tonic-gate continue; 3454*7c478bd9Sstevel@tonic-gate } 3455*7c478bd9Sstevel@tonic-gate } 3456*7c478bd9Sstevel@tonic-gate 3457*7c478bd9Sstevel@tonic-gate /* successful delivery */ 3458*7c478bd9Sstevel@tonic-gate to->q_state = QS_SENT; 3459*7c478bd9Sstevel@tonic-gate to->q_statdate = curtime(); 3460*7c478bd9Sstevel@tonic-gate e->e_nsent++; 3461*7c478bd9Sstevel@tonic-gate 3462*7c478bd9Sstevel@tonic-gate /* 3463*7c478bd9Sstevel@tonic-gate ** Checkpoint the send list every few addresses 3464*7c478bd9Sstevel@tonic-gate */ 3465*7c478bd9Sstevel@tonic-gate 3466*7c478bd9Sstevel@tonic-gate if (CheckpointInterval > 0 && e->e_nsent >= CheckpointInterval) 3467*7c478bd9Sstevel@tonic-gate { 3468*7c478bd9Sstevel@tonic-gate queueup(e, false, false); 3469*7c478bd9Sstevel@tonic-gate e->e_nsent = 0; 3470*7c478bd9Sstevel@tonic-gate } 3471*7c478bd9Sstevel@tonic-gate 3472*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 3473*7c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags)) 3474*7c478bd9Sstevel@tonic-gate { 3475*7c478bd9Sstevel@tonic-gate to->q_flags |= QDELIVERED; 3476*7c478bd9Sstevel@tonic-gate to->q_status = "2.1.5"; 3477*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 3478*7c478bd9Sstevel@tonic-gate "%s... Successfully delivered\n", 3479*7c478bd9Sstevel@tonic-gate to->q_paddr); 3480*7c478bd9Sstevel@tonic-gate } 3481*7c478bd9Sstevel@tonic-gate else if (bitset(QPINGONSUCCESS, to->q_flags) && 3482*7c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags) && 3483*7c478bd9Sstevel@tonic-gate !bitset(MCIF_DSN, mci->mci_flags)) 3484*7c478bd9Sstevel@tonic-gate { 3485*7c478bd9Sstevel@tonic-gate to->q_flags |= QRELAYED; 3486*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 3487*7c478bd9Sstevel@tonic-gate "%s... relayed; expect no further notifications\n", 3488*7c478bd9Sstevel@tonic-gate to->q_paddr); 3489*7c478bd9Sstevel@tonic-gate } 3490*7c478bd9Sstevel@tonic-gate else if (IS_DLVR_NOTIFY(e) && 3491*7c478bd9Sstevel@tonic-gate !bitset(MCIF_DLVR_BY, mci->mci_flags) && 3492*7c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags) && 3493*7c478bd9Sstevel@tonic-gate (!bitset(QHASNOTIFY, to->q_flags) || 3494*7c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags) || 3495*7c478bd9Sstevel@tonic-gate bitset(QPINGONFAILURE, to->q_flags) || 3496*7c478bd9Sstevel@tonic-gate bitset(QPINGONDELAY, to->q_flags))) 3497*7c478bd9Sstevel@tonic-gate { 3498*7c478bd9Sstevel@tonic-gate /* RFC 2852, 4.1.4.2: no NOTIFY, or not NEVER */ 3499*7c478bd9Sstevel@tonic-gate to->q_flags |= QBYNRELAY; 3500*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 3501*7c478bd9Sstevel@tonic-gate "%s... Deliver-by notify: relayed\n", 3502*7c478bd9Sstevel@tonic-gate to->q_paddr); 3503*7c478bd9Sstevel@tonic-gate } 3504*7c478bd9Sstevel@tonic-gate else if (IS_DLVR_TRACE(e) && 3505*7c478bd9Sstevel@tonic-gate (!bitset(QHASNOTIFY, to->q_flags) || 3506*7c478bd9Sstevel@tonic-gate bitset(QPINGONSUCCESS, to->q_flags) || 3507*7c478bd9Sstevel@tonic-gate bitset(QPINGONFAILURE, to->q_flags) || 3508*7c478bd9Sstevel@tonic-gate bitset(QPINGONDELAY, to->q_flags)) && 3509*7c478bd9Sstevel@tonic-gate bitset(QPRIMARY, to->q_flags)) 3510*7c478bd9Sstevel@tonic-gate { 3511*7c478bd9Sstevel@tonic-gate /* RFC 2852, 4.1.4: no NOTIFY, or not NEVER */ 3512*7c478bd9Sstevel@tonic-gate to->q_flags |= QBYTRACE; 3513*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 3514*7c478bd9Sstevel@tonic-gate "%s... Deliver-By trace: relayed\n", 3515*7c478bd9Sstevel@tonic-gate to->q_paddr); 3516*7c478bd9Sstevel@tonic-gate } 3517*7c478bd9Sstevel@tonic-gate } 3518*7c478bd9Sstevel@tonic-gate 3519*7c478bd9Sstevel@tonic-gate if (bitnset(M_LMTP, m->m_flags)) 3520*7c478bd9Sstevel@tonic-gate { 3521*7c478bd9Sstevel@tonic-gate /* 3522*7c478bd9Sstevel@tonic-gate ** Global information applies to the last recipient only; 3523*7c478bd9Sstevel@tonic-gate ** clear it out to avoid bogus errors. 3524*7c478bd9Sstevel@tonic-gate */ 3525*7c478bd9Sstevel@tonic-gate 3526*7c478bd9Sstevel@tonic-gate rcode = EX_OK; 3527*7c478bd9Sstevel@tonic-gate e->e_statmsg = NULL; 3528*7c478bd9Sstevel@tonic-gate 3529*7c478bd9Sstevel@tonic-gate /* reset the mci state for the next transaction */ 3530*7c478bd9Sstevel@tonic-gate if (mci != NULL && 3531*7c478bd9Sstevel@tonic-gate (mci->mci_state == MCIS_MAIL || 3532*7c478bd9Sstevel@tonic-gate mci->mci_state == MCIS_RCPT || 3533*7c478bd9Sstevel@tonic-gate mci->mci_state == MCIS_DATA)) 3534*7c478bd9Sstevel@tonic-gate { 3535*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_OPEN; 3536*7c478bd9Sstevel@tonic-gate SmtpPhase = mci->mci_phase = "idle"; 3537*7c478bd9Sstevel@tonic-gate sm_setproctitle(true, e, "%s: %s", CurHostName, 3538*7c478bd9Sstevel@tonic-gate mci->mci_phase); 3539*7c478bd9Sstevel@tonic-gate } 3540*7c478bd9Sstevel@tonic-gate } 3541*7c478bd9Sstevel@tonic-gate 3542*7c478bd9Sstevel@tonic-gate if (tobuf[0] != '\0') 3543*7c478bd9Sstevel@tonic-gate { 3544*7c478bd9Sstevel@tonic-gate giveresponse(rcode, NULL, m, mci, ctladdr, xstart, e, tochain); 3545*7c478bd9Sstevel@tonic-gate #if 0 3546*7c478bd9Sstevel@tonic-gate /* 3547*7c478bd9Sstevel@tonic-gate ** This code is disabled for now because I am not 3548*7c478bd9Sstevel@tonic-gate ** sure that copying status from the first recipient 3549*7c478bd9Sstevel@tonic-gate ** to all non-status'ed recipients is a good idea. 3550*7c478bd9Sstevel@tonic-gate */ 3551*7c478bd9Sstevel@tonic-gate 3552*7c478bd9Sstevel@tonic-gate if (tochain->q_message != NULL && 3553*7c478bd9Sstevel@tonic-gate !bitnset(M_LMTP, m->m_flags) && rcode != EX_OK) 3554*7c478bd9Sstevel@tonic-gate { 3555*7c478bd9Sstevel@tonic-gate for (to = tochain->q_tchain; to != NULL; 3556*7c478bd9Sstevel@tonic-gate to = to->q_tchain) 3557*7c478bd9Sstevel@tonic-gate { 3558*7c478bd9Sstevel@tonic-gate /* see if address already marked */ 3559*7c478bd9Sstevel@tonic-gate if (QS_IS_QUEUEUP(to->q_state) && 3560*7c478bd9Sstevel@tonic-gate to->q_message == NULL) 3561*7c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 3562*7c478bd9Sstevel@tonic-gate tochain->q_message); 3563*7c478bd9Sstevel@tonic-gate } 3564*7c478bd9Sstevel@tonic-gate } 3565*7c478bd9Sstevel@tonic-gate #endif /* 0 */ 3566*7c478bd9Sstevel@tonic-gate } 3567*7c478bd9Sstevel@tonic-gate if (anyok) 3568*7c478bd9Sstevel@tonic-gate markstats(e, tochain, STATS_NORMAL); 3569*7c478bd9Sstevel@tonic-gate mci_store_persistent(mci); 3570*7c478bd9Sstevel@tonic-gate 3571*7c478bd9Sstevel@tonic-gate /* Some recipients were tempfailed, try them on the next host */ 3572*7c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_retryrcpt && nummxhosts > hostnum) 3573*7c478bd9Sstevel@tonic-gate { 3574*7c478bd9Sstevel@tonic-gate /* try next MX site */ 3575*7c478bd9Sstevel@tonic-gate goto tryhost; 3576*7c478bd9Sstevel@tonic-gate } 3577*7c478bd9Sstevel@tonic-gate 3578*7c478bd9Sstevel@tonic-gate /* now close the connection */ 3579*7c478bd9Sstevel@tonic-gate if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && 3580*7c478bd9Sstevel@tonic-gate !bitset(MCIF_CACHED, mci->mci_flags)) 3581*7c478bd9Sstevel@tonic-gate smtpquit(m, mci, e); 3582*7c478bd9Sstevel@tonic-gate 3583*7c478bd9Sstevel@tonic-gate cleanup: ; 3584*7c478bd9Sstevel@tonic-gate } 3585*7c478bd9Sstevel@tonic-gate SM_FINALLY 3586*7c478bd9Sstevel@tonic-gate { 3587*7c478bd9Sstevel@tonic-gate /* 3588*7c478bd9Sstevel@tonic-gate ** Restore state and return. 3589*7c478bd9Sstevel@tonic-gate */ 3590*7c478bd9Sstevel@tonic-gate #if XDEBUG 3591*7c478bd9Sstevel@tonic-gate char wbuf[MAXLINE]; 3592*7c478bd9Sstevel@tonic-gate 3593*7c478bd9Sstevel@tonic-gate /* make absolutely certain 0, 1, and 2 are in use */ 3594*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(wbuf, sizeof wbuf, 3595*7c478bd9Sstevel@tonic-gate "%s... end of deliver(%s)", 3596*7c478bd9Sstevel@tonic-gate e->e_to == NULL ? "NO-TO-LIST" 3597*7c478bd9Sstevel@tonic-gate : shortenstring(e->e_to, 3598*7c478bd9Sstevel@tonic-gate MAXSHORTSTR), 3599*7c478bd9Sstevel@tonic-gate m->m_name); 3600*7c478bd9Sstevel@tonic-gate checkfd012(wbuf); 3601*7c478bd9Sstevel@tonic-gate #endif /* XDEBUG */ 3602*7c478bd9Sstevel@tonic-gate 3603*7c478bd9Sstevel@tonic-gate errno = 0; 3604*7c478bd9Sstevel@tonic-gate 3605*7c478bd9Sstevel@tonic-gate /* 3606*7c478bd9Sstevel@tonic-gate ** It was originally necessary to set macro 'g' to NULL 3607*7c478bd9Sstevel@tonic-gate ** because it previously pointed to an auto buffer. 3608*7c478bd9Sstevel@tonic-gate ** We don't do this any more, so this may be unnecessary. 3609*7c478bd9Sstevel@tonic-gate */ 3610*7c478bd9Sstevel@tonic-gate 3611*7c478bd9Sstevel@tonic-gate macdefine(&e->e_macro, A_PERM, 'g', (char *) NULL); 3612*7c478bd9Sstevel@tonic-gate e->e_to = NULL; 3613*7c478bd9Sstevel@tonic-gate } 3614*7c478bd9Sstevel@tonic-gate SM_END_TRY 3615*7c478bd9Sstevel@tonic-gate return rcode; 3616*7c478bd9Sstevel@tonic-gate } 3617*7c478bd9Sstevel@tonic-gate 3618*7c478bd9Sstevel@tonic-gate /* 3619*7c478bd9Sstevel@tonic-gate ** MARKFAILURE -- mark a failure on a specific address. 3620*7c478bd9Sstevel@tonic-gate ** 3621*7c478bd9Sstevel@tonic-gate ** Parameters: 3622*7c478bd9Sstevel@tonic-gate ** e -- the envelope we are sending. 3623*7c478bd9Sstevel@tonic-gate ** q -- the address to mark. 3624*7c478bd9Sstevel@tonic-gate ** mci -- mailer connection information. 3625*7c478bd9Sstevel@tonic-gate ** rcode -- the code signifying the particular failure. 3626*7c478bd9Sstevel@tonic-gate ** ovr -- override an existing code? 3627*7c478bd9Sstevel@tonic-gate ** 3628*7c478bd9Sstevel@tonic-gate ** Returns: 3629*7c478bd9Sstevel@tonic-gate ** none. 3630*7c478bd9Sstevel@tonic-gate ** 3631*7c478bd9Sstevel@tonic-gate ** Side Effects: 3632*7c478bd9Sstevel@tonic-gate ** marks the address (and possibly the envelope) with the 3633*7c478bd9Sstevel@tonic-gate ** failure so that an error will be returned or 3634*7c478bd9Sstevel@tonic-gate ** the message will be queued, as appropriate. 3635*7c478bd9Sstevel@tonic-gate */ 3636*7c478bd9Sstevel@tonic-gate 3637*7c478bd9Sstevel@tonic-gate void 3638*7c478bd9Sstevel@tonic-gate markfailure(e, q, mci, rcode, ovr) 3639*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 3640*7c478bd9Sstevel@tonic-gate register ADDRESS *q; 3641*7c478bd9Sstevel@tonic-gate register MCI *mci; 3642*7c478bd9Sstevel@tonic-gate int rcode; 3643*7c478bd9Sstevel@tonic-gate bool ovr; 3644*7c478bd9Sstevel@tonic-gate { 3645*7c478bd9Sstevel@tonic-gate int save_errno = errno; 3646*7c478bd9Sstevel@tonic-gate char *status = NULL; 3647*7c478bd9Sstevel@tonic-gate char *rstatus = NULL; 3648*7c478bd9Sstevel@tonic-gate 3649*7c478bd9Sstevel@tonic-gate switch (rcode) 3650*7c478bd9Sstevel@tonic-gate { 3651*7c478bd9Sstevel@tonic-gate case EX_OK: 3652*7c478bd9Sstevel@tonic-gate break; 3653*7c478bd9Sstevel@tonic-gate 3654*7c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 3655*7c478bd9Sstevel@tonic-gate case EX_IOERR: 3656*7c478bd9Sstevel@tonic-gate case EX_OSERR: 3657*7c478bd9Sstevel@tonic-gate q->q_state = QS_QUEUEUP; 3658*7c478bd9Sstevel@tonic-gate break; 3659*7c478bd9Sstevel@tonic-gate 3660*7c478bd9Sstevel@tonic-gate default: 3661*7c478bd9Sstevel@tonic-gate q->q_state = QS_BADADDR; 3662*7c478bd9Sstevel@tonic-gate break; 3663*7c478bd9Sstevel@tonic-gate } 3664*7c478bd9Sstevel@tonic-gate 3665*7c478bd9Sstevel@tonic-gate /* find most specific error code possible */ 3666*7c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_status != NULL) 3667*7c478bd9Sstevel@tonic-gate { 3668*7c478bd9Sstevel@tonic-gate status = sm_rpool_strdup_x(e->e_rpool, mci->mci_status); 3669*7c478bd9Sstevel@tonic-gate if (mci->mci_rstatus != NULL) 3670*7c478bd9Sstevel@tonic-gate rstatus = sm_rpool_strdup_x(e->e_rpool, 3671*7c478bd9Sstevel@tonic-gate mci->mci_rstatus); 3672*7c478bd9Sstevel@tonic-gate else 3673*7c478bd9Sstevel@tonic-gate rstatus = NULL; 3674*7c478bd9Sstevel@tonic-gate } 3675*7c478bd9Sstevel@tonic-gate else if (e->e_status != NULL) 3676*7c478bd9Sstevel@tonic-gate { 3677*7c478bd9Sstevel@tonic-gate status = e->e_status; 3678*7c478bd9Sstevel@tonic-gate rstatus = NULL; 3679*7c478bd9Sstevel@tonic-gate } 3680*7c478bd9Sstevel@tonic-gate else 3681*7c478bd9Sstevel@tonic-gate { 3682*7c478bd9Sstevel@tonic-gate switch (rcode) 3683*7c478bd9Sstevel@tonic-gate { 3684*7c478bd9Sstevel@tonic-gate case EX_USAGE: 3685*7c478bd9Sstevel@tonic-gate status = "5.5.4"; 3686*7c478bd9Sstevel@tonic-gate break; 3687*7c478bd9Sstevel@tonic-gate 3688*7c478bd9Sstevel@tonic-gate case EX_DATAERR: 3689*7c478bd9Sstevel@tonic-gate status = "5.5.2"; 3690*7c478bd9Sstevel@tonic-gate break; 3691*7c478bd9Sstevel@tonic-gate 3692*7c478bd9Sstevel@tonic-gate case EX_NOUSER: 3693*7c478bd9Sstevel@tonic-gate status = "5.1.1"; 3694*7c478bd9Sstevel@tonic-gate break; 3695*7c478bd9Sstevel@tonic-gate 3696*7c478bd9Sstevel@tonic-gate case EX_NOHOST: 3697*7c478bd9Sstevel@tonic-gate status = "5.1.2"; 3698*7c478bd9Sstevel@tonic-gate break; 3699*7c478bd9Sstevel@tonic-gate 3700*7c478bd9Sstevel@tonic-gate case EX_NOINPUT: 3701*7c478bd9Sstevel@tonic-gate case EX_CANTCREAT: 3702*7c478bd9Sstevel@tonic-gate case EX_NOPERM: 3703*7c478bd9Sstevel@tonic-gate status = "5.3.0"; 3704*7c478bd9Sstevel@tonic-gate break; 3705*7c478bd9Sstevel@tonic-gate 3706*7c478bd9Sstevel@tonic-gate case EX_UNAVAILABLE: 3707*7c478bd9Sstevel@tonic-gate case EX_SOFTWARE: 3708*7c478bd9Sstevel@tonic-gate case EX_OSFILE: 3709*7c478bd9Sstevel@tonic-gate case EX_PROTOCOL: 3710*7c478bd9Sstevel@tonic-gate case EX_CONFIG: 3711*7c478bd9Sstevel@tonic-gate status = "5.5.0"; 3712*7c478bd9Sstevel@tonic-gate break; 3713*7c478bd9Sstevel@tonic-gate 3714*7c478bd9Sstevel@tonic-gate case EX_OSERR: 3715*7c478bd9Sstevel@tonic-gate case EX_IOERR: 3716*7c478bd9Sstevel@tonic-gate status = "4.5.0"; 3717*7c478bd9Sstevel@tonic-gate break; 3718*7c478bd9Sstevel@tonic-gate 3719*7c478bd9Sstevel@tonic-gate case EX_TEMPFAIL: 3720*7c478bd9Sstevel@tonic-gate status = "4.2.0"; 3721*7c478bd9Sstevel@tonic-gate break; 3722*7c478bd9Sstevel@tonic-gate } 3723*7c478bd9Sstevel@tonic-gate } 3724*7c478bd9Sstevel@tonic-gate 3725*7c478bd9Sstevel@tonic-gate /* new status? */ 3726*7c478bd9Sstevel@tonic-gate if (status != NULL && *status != '\0' && (ovr || q->q_status == NULL || 3727*7c478bd9Sstevel@tonic-gate *q->q_status == '\0' || *q->q_status < *status)) 3728*7c478bd9Sstevel@tonic-gate { 3729*7c478bd9Sstevel@tonic-gate q->q_status = status; 3730*7c478bd9Sstevel@tonic-gate q->q_rstatus = rstatus; 3731*7c478bd9Sstevel@tonic-gate } 3732*7c478bd9Sstevel@tonic-gate if (rcode != EX_OK && q->q_rstatus == NULL && 3733*7c478bd9Sstevel@tonic-gate q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && 3734*7c478bd9Sstevel@tonic-gate sm_strcasecmp(q->q_mailer->m_diagtype, "X-UNIX") == 0) 3735*7c478bd9Sstevel@tonic-gate { 3736*7c478bd9Sstevel@tonic-gate char buf[16]; 3737*7c478bd9Sstevel@tonic-gate 3738*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "%d", rcode); 3739*7c478bd9Sstevel@tonic-gate q->q_rstatus = sm_rpool_strdup_x(e->e_rpool, buf); 3740*7c478bd9Sstevel@tonic-gate } 3741*7c478bd9Sstevel@tonic-gate 3742*7c478bd9Sstevel@tonic-gate q->q_statdate = curtime(); 3743*7c478bd9Sstevel@tonic-gate if (CurHostName != NULL && CurHostName[0] != '\0' && 3744*7c478bd9Sstevel@tonic-gate mci != NULL && !bitset(M_LOCALMAILER, mci->mci_flags)) 3745*7c478bd9Sstevel@tonic-gate q->q_statmta = sm_rpool_strdup_x(e->e_rpool, CurHostName); 3746*7c478bd9Sstevel@tonic-gate 3747*7c478bd9Sstevel@tonic-gate /* restore errno */ 3748*7c478bd9Sstevel@tonic-gate errno = save_errno; 3749*7c478bd9Sstevel@tonic-gate } 3750*7c478bd9Sstevel@tonic-gate /* 3751*7c478bd9Sstevel@tonic-gate ** ENDMAILER -- Wait for mailer to terminate. 3752*7c478bd9Sstevel@tonic-gate ** 3753*7c478bd9Sstevel@tonic-gate ** We should never get fatal errors (e.g., segmentation 3754*7c478bd9Sstevel@tonic-gate ** violation), so we report those specially. For other 3755*7c478bd9Sstevel@tonic-gate ** errors, we choose a status message (into statmsg), 3756*7c478bd9Sstevel@tonic-gate ** and if it represents an error, we print it. 3757*7c478bd9Sstevel@tonic-gate ** 3758*7c478bd9Sstevel@tonic-gate ** Parameters: 3759*7c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 3760*7c478bd9Sstevel@tonic-gate ** e -- the current envelope. 3761*7c478bd9Sstevel@tonic-gate ** pv -- the parameter vector that invoked the mailer 3762*7c478bd9Sstevel@tonic-gate ** (for error messages). 3763*7c478bd9Sstevel@tonic-gate ** 3764*7c478bd9Sstevel@tonic-gate ** Returns: 3765*7c478bd9Sstevel@tonic-gate ** exit code of mailer. 3766*7c478bd9Sstevel@tonic-gate ** 3767*7c478bd9Sstevel@tonic-gate ** Side Effects: 3768*7c478bd9Sstevel@tonic-gate ** none. 3769*7c478bd9Sstevel@tonic-gate */ 3770*7c478bd9Sstevel@tonic-gate 3771*7c478bd9Sstevel@tonic-gate static jmp_buf EndWaitTimeout; 3772*7c478bd9Sstevel@tonic-gate 3773*7c478bd9Sstevel@tonic-gate static void 3774*7c478bd9Sstevel@tonic-gate endwaittimeout(ignore) 3775*7c478bd9Sstevel@tonic-gate int ignore; 3776*7c478bd9Sstevel@tonic-gate { 3777*7c478bd9Sstevel@tonic-gate /* 3778*7c478bd9Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 3779*7c478bd9Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 3780*7c478bd9Sstevel@tonic-gate ** DOING. 3781*7c478bd9Sstevel@tonic-gate */ 3782*7c478bd9Sstevel@tonic-gate 3783*7c478bd9Sstevel@tonic-gate errno = ETIMEDOUT; 3784*7c478bd9Sstevel@tonic-gate longjmp(EndWaitTimeout, 1); 3785*7c478bd9Sstevel@tonic-gate } 3786*7c478bd9Sstevel@tonic-gate 3787*7c478bd9Sstevel@tonic-gate int 3788*7c478bd9Sstevel@tonic-gate endmailer(mci, e, pv) 3789*7c478bd9Sstevel@tonic-gate register MCI *mci; 3790*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 3791*7c478bd9Sstevel@tonic-gate char **pv; 3792*7c478bd9Sstevel@tonic-gate { 3793*7c478bd9Sstevel@tonic-gate int st; 3794*7c478bd9Sstevel@tonic-gate int save_errno = errno; 3795*7c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 3796*7c478bd9Sstevel@tonic-gate SM_EVENT *ev = NULL; 3797*7c478bd9Sstevel@tonic-gate 3798*7c478bd9Sstevel@tonic-gate 3799*7c478bd9Sstevel@tonic-gate mci_unlock_host(mci); 3800*7c478bd9Sstevel@tonic-gate 3801*7c478bd9Sstevel@tonic-gate /* close output to mailer */ 3802*7c478bd9Sstevel@tonic-gate if (mci->mci_out != NULL) 3803*7c478bd9Sstevel@tonic-gate { 3804*7c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 3805*7c478bd9Sstevel@tonic-gate mci->mci_out = NULL; 3806*7c478bd9Sstevel@tonic-gate } 3807*7c478bd9Sstevel@tonic-gate 3808*7c478bd9Sstevel@tonic-gate /* copy any remaining input to transcript */ 3809*7c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL && mci->mci_state != MCIS_ERROR && 3810*7c478bd9Sstevel@tonic-gate e->e_xfp != NULL) 3811*7c478bd9Sstevel@tonic-gate { 3812*7c478bd9Sstevel@tonic-gate while (sfgets(buf, sizeof buf, mci->mci_in, 3813*7c478bd9Sstevel@tonic-gate TimeOuts.to_quit, "Draining Input") != NULL) 3814*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(e->e_xfp, SM_TIME_DEFAULT, buf); 3815*7c478bd9Sstevel@tonic-gate } 3816*7c478bd9Sstevel@tonic-gate 3817*7c478bd9Sstevel@tonic-gate #if SASL 3818*7c478bd9Sstevel@tonic-gate /* close SASL connection */ 3819*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_AUTHACT, mci->mci_flags)) 3820*7c478bd9Sstevel@tonic-gate { 3821*7c478bd9Sstevel@tonic-gate sasl_dispose(&mci->mci_conn); 3822*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_AUTHACT; 3823*7c478bd9Sstevel@tonic-gate } 3824*7c478bd9Sstevel@tonic-gate #endif /* SASL */ 3825*7c478bd9Sstevel@tonic-gate 3826*7c478bd9Sstevel@tonic-gate #if STARTTLS 3827*7c478bd9Sstevel@tonic-gate /* shutdown TLS */ 3828*7c478bd9Sstevel@tonic-gate (void) endtlsclt(mci); 3829*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */ 3830*7c478bd9Sstevel@tonic-gate 3831*7c478bd9Sstevel@tonic-gate /* now close the input */ 3832*7c478bd9Sstevel@tonic-gate if (mci->mci_in != NULL) 3833*7c478bd9Sstevel@tonic-gate { 3834*7c478bd9Sstevel@tonic-gate (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 3835*7c478bd9Sstevel@tonic-gate mci->mci_in = NULL; 3836*7c478bd9Sstevel@tonic-gate } 3837*7c478bd9Sstevel@tonic-gate mci->mci_state = MCIS_CLOSED; 3838*7c478bd9Sstevel@tonic-gate 3839*7c478bd9Sstevel@tonic-gate errno = save_errno; 3840*7c478bd9Sstevel@tonic-gate 3841*7c478bd9Sstevel@tonic-gate /* in the IPC case there is nothing to wait for */ 3842*7c478bd9Sstevel@tonic-gate if (mci->mci_pid == 0) 3843*7c478bd9Sstevel@tonic-gate return EX_OK; 3844*7c478bd9Sstevel@tonic-gate 3845*7c478bd9Sstevel@tonic-gate /* put a timeout around the wait */ 3846*7c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_wait > 0) 3847*7c478bd9Sstevel@tonic-gate { 3848*7c478bd9Sstevel@tonic-gate if (setjmp(EndWaitTimeout) == 0) 3849*7c478bd9Sstevel@tonic-gate ev = sm_setevent(mci->mci_mailer->m_wait, 3850*7c478bd9Sstevel@tonic-gate endwaittimeout, 0); 3851*7c478bd9Sstevel@tonic-gate else 3852*7c478bd9Sstevel@tonic-gate { 3853*7c478bd9Sstevel@tonic-gate syserr("endmailer %s: wait timeout (%ld)", 3854*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name, 3855*7c478bd9Sstevel@tonic-gate (long) mci->mci_mailer->m_wait); 3856*7c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 3857*7c478bd9Sstevel@tonic-gate } 3858*7c478bd9Sstevel@tonic-gate } 3859*7c478bd9Sstevel@tonic-gate 3860*7c478bd9Sstevel@tonic-gate /* wait for the mailer process, collect status */ 3861*7c478bd9Sstevel@tonic-gate st = waitfor(mci->mci_pid); 3862*7c478bd9Sstevel@tonic-gate save_errno = errno; 3863*7c478bd9Sstevel@tonic-gate if (ev != NULL) 3864*7c478bd9Sstevel@tonic-gate sm_clrevent(ev); 3865*7c478bd9Sstevel@tonic-gate errno = save_errno; 3866*7c478bd9Sstevel@tonic-gate 3867*7c478bd9Sstevel@tonic-gate if (st == -1) 3868*7c478bd9Sstevel@tonic-gate { 3869*7c478bd9Sstevel@tonic-gate syserr("endmailer %s: wait", mci->mci_mailer->m_name); 3870*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 3871*7c478bd9Sstevel@tonic-gate } 3872*7c478bd9Sstevel@tonic-gate 3873*7c478bd9Sstevel@tonic-gate if (WIFEXITED(st)) 3874*7c478bd9Sstevel@tonic-gate { 3875*7c478bd9Sstevel@tonic-gate /* normal death -- return status */ 3876*7c478bd9Sstevel@tonic-gate return (WEXITSTATUS(st)); 3877*7c478bd9Sstevel@tonic-gate } 3878*7c478bd9Sstevel@tonic-gate 3879*7c478bd9Sstevel@tonic-gate /* it died a horrid death */ 3880*7c478bd9Sstevel@tonic-gate syserr("451 4.3.0 mailer %s died with signal %d%s", 3881*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_name, WTERMSIG(st), 3882*7c478bd9Sstevel@tonic-gate WCOREDUMP(st) ? " (core dumped)" : 3883*7c478bd9Sstevel@tonic-gate (WIFSTOPPED(st) ? " (stopped)" : "")); 3884*7c478bd9Sstevel@tonic-gate 3885*7c478bd9Sstevel@tonic-gate /* log the arguments */ 3886*7c478bd9Sstevel@tonic-gate if (pv != NULL && e->e_xfp != NULL) 3887*7c478bd9Sstevel@tonic-gate { 3888*7c478bd9Sstevel@tonic-gate register char **av; 3889*7c478bd9Sstevel@tonic-gate 3890*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "Arguments:"); 3891*7c478bd9Sstevel@tonic-gate for (av = pv; *av != NULL; av++) 3892*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, " %s", 3893*7c478bd9Sstevel@tonic-gate *av); 3894*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "\n"); 3895*7c478bd9Sstevel@tonic-gate } 3896*7c478bd9Sstevel@tonic-gate 3897*7c478bd9Sstevel@tonic-gate ExitStat = EX_TEMPFAIL; 3898*7c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 3899*7c478bd9Sstevel@tonic-gate } 3900*7c478bd9Sstevel@tonic-gate /* 3901*7c478bd9Sstevel@tonic-gate ** GIVERESPONSE -- Interpret an error response from a mailer 3902*7c478bd9Sstevel@tonic-gate ** 3903*7c478bd9Sstevel@tonic-gate ** Parameters: 3904*7c478bd9Sstevel@tonic-gate ** status -- the status code from the mailer (high byte 3905*7c478bd9Sstevel@tonic-gate ** only; core dumps must have been taken care of 3906*7c478bd9Sstevel@tonic-gate ** already). 3907*7c478bd9Sstevel@tonic-gate ** dsn -- the DSN associated with the address, if any. 3908*7c478bd9Sstevel@tonic-gate ** m -- the mailer info for this mailer. 3909*7c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info -- can be NULL if the 3910*7c478bd9Sstevel@tonic-gate ** response is given before the connection is made. 3911*7c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address for the recipient 3912*7c478bd9Sstevel@tonic-gate ** address(es). 3913*7c478bd9Sstevel@tonic-gate ** xstart -- the transaction start time, for computing 3914*7c478bd9Sstevel@tonic-gate ** transaction delays. 3915*7c478bd9Sstevel@tonic-gate ** e -- the current envelope. 3916*7c478bd9Sstevel@tonic-gate ** to -- the current recipient (NULL if none). 3917*7c478bd9Sstevel@tonic-gate ** 3918*7c478bd9Sstevel@tonic-gate ** Returns: 3919*7c478bd9Sstevel@tonic-gate ** none. 3920*7c478bd9Sstevel@tonic-gate ** 3921*7c478bd9Sstevel@tonic-gate ** Side Effects: 3922*7c478bd9Sstevel@tonic-gate ** Errors may be incremented. 3923*7c478bd9Sstevel@tonic-gate ** ExitStat may be set. 3924*7c478bd9Sstevel@tonic-gate */ 3925*7c478bd9Sstevel@tonic-gate 3926*7c478bd9Sstevel@tonic-gate void 3927*7c478bd9Sstevel@tonic-gate giveresponse(status, dsn, m, mci, ctladdr, xstart, e, to) 3928*7c478bd9Sstevel@tonic-gate int status; 3929*7c478bd9Sstevel@tonic-gate char *dsn; 3930*7c478bd9Sstevel@tonic-gate register MAILER *m; 3931*7c478bd9Sstevel@tonic-gate register MCI *mci; 3932*7c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 3933*7c478bd9Sstevel@tonic-gate time_t xstart; 3934*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 3935*7c478bd9Sstevel@tonic-gate ADDRESS *to; 3936*7c478bd9Sstevel@tonic-gate { 3937*7c478bd9Sstevel@tonic-gate register const char *statmsg; 3938*7c478bd9Sstevel@tonic-gate int errnum = errno; 3939*7c478bd9Sstevel@tonic-gate int off = 4; 3940*7c478bd9Sstevel@tonic-gate bool usestat = false; 3941*7c478bd9Sstevel@tonic-gate char dsnbuf[ENHSCLEN]; 3942*7c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 3943*7c478bd9Sstevel@tonic-gate char *exmsg; 3944*7c478bd9Sstevel@tonic-gate 3945*7c478bd9Sstevel@tonic-gate if (e == NULL) 3946*7c478bd9Sstevel@tonic-gate syserr("giveresponse: null envelope"); 3947*7c478bd9Sstevel@tonic-gate 3948*7c478bd9Sstevel@tonic-gate /* 3949*7c478bd9Sstevel@tonic-gate ** Compute status message from code. 3950*7c478bd9Sstevel@tonic-gate */ 3951*7c478bd9Sstevel@tonic-gate 3952*7c478bd9Sstevel@tonic-gate exmsg = sm_sysexmsg(status); 3953*7c478bd9Sstevel@tonic-gate if (status == 0) 3954*7c478bd9Sstevel@tonic-gate { 3955*7c478bd9Sstevel@tonic-gate statmsg = "250 2.0.0 Sent"; 3956*7c478bd9Sstevel@tonic-gate if (e->e_statmsg != NULL) 3957*7c478bd9Sstevel@tonic-gate { 3958*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "%s (%s)", 3959*7c478bd9Sstevel@tonic-gate statmsg, 3960*7c478bd9Sstevel@tonic-gate shortenstring(e->e_statmsg, 403)); 3961*7c478bd9Sstevel@tonic-gate statmsg = buf; 3962*7c478bd9Sstevel@tonic-gate } 3963*7c478bd9Sstevel@tonic-gate } 3964*7c478bd9Sstevel@tonic-gate else if (exmsg == NULL) 3965*7c478bd9Sstevel@tonic-gate { 3966*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, 3967*7c478bd9Sstevel@tonic-gate "554 5.3.0 unknown mailer error %d", 3968*7c478bd9Sstevel@tonic-gate status); 3969*7c478bd9Sstevel@tonic-gate status = EX_UNAVAILABLE; 3970*7c478bd9Sstevel@tonic-gate statmsg = buf; 3971*7c478bd9Sstevel@tonic-gate usestat = true; 3972*7c478bd9Sstevel@tonic-gate } 3973*7c478bd9Sstevel@tonic-gate else if (status == EX_TEMPFAIL) 3974*7c478bd9Sstevel@tonic-gate { 3975*7c478bd9Sstevel@tonic-gate char *bp = buf; 3976*7c478bd9Sstevel@tonic-gate 3977*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, exmsg + 1, SPACELEFT(buf, bp)); 3978*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 3979*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 3980*7c478bd9Sstevel@tonic-gate if (h_errno == TRY_AGAIN) 3981*7c478bd9Sstevel@tonic-gate statmsg = sm_errstring(h_errno + E_DNSBASE); 3982*7c478bd9Sstevel@tonic-gate else 3983*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 3984*7c478bd9Sstevel@tonic-gate { 3985*7c478bd9Sstevel@tonic-gate if (errnum != 0) 3986*7c478bd9Sstevel@tonic-gate statmsg = sm_errstring(errnum); 3987*7c478bd9Sstevel@tonic-gate else 3988*7c478bd9Sstevel@tonic-gate statmsg = SmtpError; 3989*7c478bd9Sstevel@tonic-gate } 3990*7c478bd9Sstevel@tonic-gate if (statmsg != NULL && statmsg[0] != '\0') 3991*7c478bd9Sstevel@tonic-gate { 3992*7c478bd9Sstevel@tonic-gate switch (errnum) 3993*7c478bd9Sstevel@tonic-gate { 3994*7c478bd9Sstevel@tonic-gate #ifdef ENETDOWN 3995*7c478bd9Sstevel@tonic-gate case ENETDOWN: /* Network is down */ 3996*7c478bd9Sstevel@tonic-gate #endif /* ENETDOWN */ 3997*7c478bd9Sstevel@tonic-gate #ifdef ENETUNREACH 3998*7c478bd9Sstevel@tonic-gate case ENETUNREACH: /* Network is unreachable */ 3999*7c478bd9Sstevel@tonic-gate #endif /* ENETUNREACH */ 4000*7c478bd9Sstevel@tonic-gate #ifdef ENETRESET 4001*7c478bd9Sstevel@tonic-gate case ENETRESET: /* Network dropped connection on reset */ 4002*7c478bd9Sstevel@tonic-gate #endif /* ENETRESET */ 4003*7c478bd9Sstevel@tonic-gate #ifdef ECONNABORTED 4004*7c478bd9Sstevel@tonic-gate case ECONNABORTED: /* Software caused connection abort */ 4005*7c478bd9Sstevel@tonic-gate #endif /* ECONNABORTED */ 4006*7c478bd9Sstevel@tonic-gate #ifdef EHOSTDOWN 4007*7c478bd9Sstevel@tonic-gate case EHOSTDOWN: /* Host is down */ 4008*7c478bd9Sstevel@tonic-gate #endif /* EHOSTDOWN */ 4009*7c478bd9Sstevel@tonic-gate #ifdef EHOSTUNREACH 4010*7c478bd9Sstevel@tonic-gate case EHOSTUNREACH: /* No route to host */ 4011*7c478bd9Sstevel@tonic-gate #endif /* EHOSTUNREACH */ 4012*7c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 4013*7c478bd9Sstevel@tonic-gate { 4014*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, 4015*7c478bd9Sstevel@tonic-gate SPACELEFT(buf, bp), 4016*7c478bd9Sstevel@tonic-gate 2, ": ", 4017*7c478bd9Sstevel@tonic-gate mci->mci_host); 4018*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4019*7c478bd9Sstevel@tonic-gate } 4020*7c478bd9Sstevel@tonic-gate break; 4021*7c478bd9Sstevel@tonic-gate } 4022*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ": ", 4023*7c478bd9Sstevel@tonic-gate statmsg); 4024*7c478bd9Sstevel@tonic-gate usestat = true; 4025*7c478bd9Sstevel@tonic-gate } 4026*7c478bd9Sstevel@tonic-gate statmsg = buf; 4027*7c478bd9Sstevel@tonic-gate } 4028*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 4029*7c478bd9Sstevel@tonic-gate else if (status == EX_NOHOST && h_errno != 0) 4030*7c478bd9Sstevel@tonic-gate { 4031*7c478bd9Sstevel@tonic-gate statmsg = sm_errstring(h_errno + E_DNSBASE); 4032*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "%s (%s)", exmsg + 1, 4033*7c478bd9Sstevel@tonic-gate statmsg); 4034*7c478bd9Sstevel@tonic-gate statmsg = buf; 4035*7c478bd9Sstevel@tonic-gate usestat = true; 4036*7c478bd9Sstevel@tonic-gate } 4037*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 4038*7c478bd9Sstevel@tonic-gate else 4039*7c478bd9Sstevel@tonic-gate { 4040*7c478bd9Sstevel@tonic-gate statmsg = exmsg; 4041*7c478bd9Sstevel@tonic-gate if (*statmsg++ == ':' && errnum != 0) 4042*7c478bd9Sstevel@tonic-gate { 4043*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "%s: %s", statmsg, 4044*7c478bd9Sstevel@tonic-gate sm_errstring(errnum)); 4045*7c478bd9Sstevel@tonic-gate statmsg = buf; 4046*7c478bd9Sstevel@tonic-gate usestat = true; 4047*7c478bd9Sstevel@tonic-gate } 4048*7c478bd9Sstevel@tonic-gate else if (bitnset(M_LMTP, m->m_flags) && e->e_statmsg != NULL) 4049*7c478bd9Sstevel@tonic-gate { 4050*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "%s (%s)", statmsg, 4051*7c478bd9Sstevel@tonic-gate shortenstring(e->e_statmsg, 403)); 4052*7c478bd9Sstevel@tonic-gate statmsg = buf; 4053*7c478bd9Sstevel@tonic-gate usestat = true; 4054*7c478bd9Sstevel@tonic-gate } 4055*7c478bd9Sstevel@tonic-gate } 4056*7c478bd9Sstevel@tonic-gate 4057*7c478bd9Sstevel@tonic-gate /* 4058*7c478bd9Sstevel@tonic-gate ** Print the message as appropriate 4059*7c478bd9Sstevel@tonic-gate */ 4060*7c478bd9Sstevel@tonic-gate 4061*7c478bd9Sstevel@tonic-gate if (status == EX_OK || status == EX_TEMPFAIL) 4062*7c478bd9Sstevel@tonic-gate { 4063*7c478bd9Sstevel@tonic-gate extern char MsgBuf[]; 4064*7c478bd9Sstevel@tonic-gate 4065*7c478bd9Sstevel@tonic-gate if ((off = isenhsc(statmsg + 4, ' ')) > 0) 4066*7c478bd9Sstevel@tonic-gate { 4067*7c478bd9Sstevel@tonic-gate if (dsn == NULL) 4068*7c478bd9Sstevel@tonic-gate { 4069*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 4070*7c478bd9Sstevel@tonic-gate "%.*s", off, statmsg + 4); 4071*7c478bd9Sstevel@tonic-gate dsn = dsnbuf; 4072*7c478bd9Sstevel@tonic-gate } 4073*7c478bd9Sstevel@tonic-gate off += 5; 4074*7c478bd9Sstevel@tonic-gate } 4075*7c478bd9Sstevel@tonic-gate else 4076*7c478bd9Sstevel@tonic-gate { 4077*7c478bd9Sstevel@tonic-gate off = 4; 4078*7c478bd9Sstevel@tonic-gate } 4079*7c478bd9Sstevel@tonic-gate message("%s", statmsg + off); 4080*7c478bd9Sstevel@tonic-gate if (status == EX_TEMPFAIL && e->e_xfp != NULL) 4081*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, "%s\n", 4082*7c478bd9Sstevel@tonic-gate &MsgBuf[4]); 4083*7c478bd9Sstevel@tonic-gate } 4084*7c478bd9Sstevel@tonic-gate else 4085*7c478bd9Sstevel@tonic-gate { 4086*7c478bd9Sstevel@tonic-gate char mbuf[ENHSCLEN + 4]; 4087*7c478bd9Sstevel@tonic-gate 4088*7c478bd9Sstevel@tonic-gate Errors++; 4089*7c478bd9Sstevel@tonic-gate if ((off = isenhsc(statmsg + 4, ' ')) > 0 && 4090*7c478bd9Sstevel@tonic-gate off < sizeof mbuf - 4) 4091*7c478bd9Sstevel@tonic-gate { 4092*7c478bd9Sstevel@tonic-gate if (dsn == NULL) 4093*7c478bd9Sstevel@tonic-gate { 4094*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(dsnbuf, sizeof dsnbuf, 4095*7c478bd9Sstevel@tonic-gate "%.*s", off, statmsg + 4); 4096*7c478bd9Sstevel@tonic-gate dsn = dsnbuf; 4097*7c478bd9Sstevel@tonic-gate } 4098*7c478bd9Sstevel@tonic-gate off += 5; 4099*7c478bd9Sstevel@tonic-gate 4100*7c478bd9Sstevel@tonic-gate /* copy only part of statmsg to mbuf */ 4101*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(mbuf, statmsg, off); 4102*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(mbuf, " %s", sizeof mbuf); 4103*7c478bd9Sstevel@tonic-gate } 4104*7c478bd9Sstevel@tonic-gate else 4105*7c478bd9Sstevel@tonic-gate { 4106*7c478bd9Sstevel@tonic-gate dsnbuf[0] = '\0'; 4107*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(mbuf, sizeof mbuf, "%.3s %%s", 4108*7c478bd9Sstevel@tonic-gate statmsg); 4109*7c478bd9Sstevel@tonic-gate off = 4; 4110*7c478bd9Sstevel@tonic-gate } 4111*7c478bd9Sstevel@tonic-gate usrerr(mbuf, &statmsg[off]); 4112*7c478bd9Sstevel@tonic-gate } 4113*7c478bd9Sstevel@tonic-gate 4114*7c478bd9Sstevel@tonic-gate /* 4115*7c478bd9Sstevel@tonic-gate ** Final cleanup. 4116*7c478bd9Sstevel@tonic-gate ** Log a record of the transaction. Compute the new 4117*7c478bd9Sstevel@tonic-gate ** ExitStat -- if we already had an error, stick with 4118*7c478bd9Sstevel@tonic-gate ** that. 4119*7c478bd9Sstevel@tonic-gate */ 4120*7c478bd9Sstevel@tonic-gate 4121*7c478bd9Sstevel@tonic-gate if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) && 4122*7c478bd9Sstevel@tonic-gate LogLevel > ((status == EX_TEMPFAIL) ? 8 : (status == EX_OK) ? 7 : 6)) 4123*7c478bd9Sstevel@tonic-gate logdelivery(m, mci, dsn, statmsg + off, ctladdr, xstart, e); 4124*7c478bd9Sstevel@tonic-gate 4125*7c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 4126*7c478bd9Sstevel@tonic-gate sm_dprintf("giveresponse: status=%d, dsn=%s, e->e_message=%s, errnum=%d\n", 4127*7c478bd9Sstevel@tonic-gate status, 4128*7c478bd9Sstevel@tonic-gate dsn == NULL ? "<NULL>" : dsn, 4129*7c478bd9Sstevel@tonic-gate e->e_message == NULL ? "<NULL>" : e->e_message, 4130*7c478bd9Sstevel@tonic-gate errnum); 4131*7c478bd9Sstevel@tonic-gate 4132*7c478bd9Sstevel@tonic-gate if (status != EX_TEMPFAIL) 4133*7c478bd9Sstevel@tonic-gate setstat(status); 4134*7c478bd9Sstevel@tonic-gate if (status != EX_OK && (status != EX_TEMPFAIL || e->e_message == NULL)) 4135*7c478bd9Sstevel@tonic-gate e->e_message = sm_rpool_strdup_x(e->e_rpool, statmsg + off); 4136*7c478bd9Sstevel@tonic-gate if (status != EX_OK && to != NULL && to->q_message == NULL) 4137*7c478bd9Sstevel@tonic-gate { 4138*7c478bd9Sstevel@tonic-gate if (!usestat && e->e_message != NULL) 4139*7c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 4140*7c478bd9Sstevel@tonic-gate e->e_message); 4141*7c478bd9Sstevel@tonic-gate else 4142*7c478bd9Sstevel@tonic-gate to->q_message = sm_rpool_strdup_x(e->e_rpool, 4143*7c478bd9Sstevel@tonic-gate statmsg + off); 4144*7c478bd9Sstevel@tonic-gate } 4145*7c478bd9Sstevel@tonic-gate errno = 0; 4146*7c478bd9Sstevel@tonic-gate SM_SET_H_ERRNO(0); 4147*7c478bd9Sstevel@tonic-gate } 4148*7c478bd9Sstevel@tonic-gate /* 4149*7c478bd9Sstevel@tonic-gate ** LOGDELIVERY -- log the delivery in the system log 4150*7c478bd9Sstevel@tonic-gate ** 4151*7c478bd9Sstevel@tonic-gate ** Care is taken to avoid logging lines that are too long, because 4152*7c478bd9Sstevel@tonic-gate ** some versions of syslog have an unfortunate proclivity for core 4153*7c478bd9Sstevel@tonic-gate ** dumping. This is a hack, to be sure, that is at best empirical. 4154*7c478bd9Sstevel@tonic-gate ** 4155*7c478bd9Sstevel@tonic-gate ** Parameters: 4156*7c478bd9Sstevel@tonic-gate ** m -- the mailer info. Can be NULL for initial queue. 4157*7c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info -- can be NULL if the 4158*7c478bd9Sstevel@tonic-gate ** log is occurring when no connection is active. 4159*7c478bd9Sstevel@tonic-gate ** dsn -- the DSN attached to the status. 4160*7c478bd9Sstevel@tonic-gate ** status -- the message to print for the status. 4161*7c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address for the to list. 4162*7c478bd9Sstevel@tonic-gate ** xstart -- the transaction start time, used for 4163*7c478bd9Sstevel@tonic-gate ** computing transaction delay. 4164*7c478bd9Sstevel@tonic-gate ** e -- the current envelope. 4165*7c478bd9Sstevel@tonic-gate ** 4166*7c478bd9Sstevel@tonic-gate ** Returns: 4167*7c478bd9Sstevel@tonic-gate ** none 4168*7c478bd9Sstevel@tonic-gate ** 4169*7c478bd9Sstevel@tonic-gate ** Side Effects: 4170*7c478bd9Sstevel@tonic-gate ** none 4171*7c478bd9Sstevel@tonic-gate */ 4172*7c478bd9Sstevel@tonic-gate 4173*7c478bd9Sstevel@tonic-gate void 4174*7c478bd9Sstevel@tonic-gate logdelivery(m, mci, dsn, status, ctladdr, xstart, e) 4175*7c478bd9Sstevel@tonic-gate MAILER *m; 4176*7c478bd9Sstevel@tonic-gate register MCI *mci; 4177*7c478bd9Sstevel@tonic-gate char *dsn; 4178*7c478bd9Sstevel@tonic-gate const char *status; 4179*7c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 4180*7c478bd9Sstevel@tonic-gate time_t xstart; 4181*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 4182*7c478bd9Sstevel@tonic-gate { 4183*7c478bd9Sstevel@tonic-gate register char *bp; 4184*7c478bd9Sstevel@tonic-gate register char *p; 4185*7c478bd9Sstevel@tonic-gate int l; 4186*7c478bd9Sstevel@tonic-gate time_t now = curtime(); 4187*7c478bd9Sstevel@tonic-gate char buf[1024]; 4188*7c478bd9Sstevel@tonic-gate 4189*7c478bd9Sstevel@tonic-gate #if (SYSLOG_BUFSIZE) >= 256 4190*7c478bd9Sstevel@tonic-gate /* ctladdr: max 106 bytes */ 4191*7c478bd9Sstevel@tonic-gate bp = buf; 4192*7c478bd9Sstevel@tonic-gate if (ctladdr != NULL) 4193*7c478bd9Sstevel@tonic-gate { 4194*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", ctladdr=", 4195*7c478bd9Sstevel@tonic-gate shortenstring(ctladdr->q_paddr, 83)); 4196*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4197*7c478bd9Sstevel@tonic-gate if (bitset(QGOODUID, ctladdr->q_flags)) 4198*7c478bd9Sstevel@tonic-gate { 4199*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 4200*7c478bd9Sstevel@tonic-gate (int) ctladdr->q_uid, 4201*7c478bd9Sstevel@tonic-gate (int) ctladdr->q_gid); 4202*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4203*7c478bd9Sstevel@tonic-gate } 4204*7c478bd9Sstevel@tonic-gate } 4205*7c478bd9Sstevel@tonic-gate 4206*7c478bd9Sstevel@tonic-gate /* delay & xdelay: max 41 bytes */ 4207*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", delay=", 4208*7c478bd9Sstevel@tonic-gate pintvl(now - e->e_ctime, true)); 4209*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4210*7c478bd9Sstevel@tonic-gate 4211*7c478bd9Sstevel@tonic-gate if (xstart != (time_t) 0) 4212*7c478bd9Sstevel@tonic-gate { 4213*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 4214*7c478bd9Sstevel@tonic-gate pintvl(now - xstart, true)); 4215*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4216*7c478bd9Sstevel@tonic-gate } 4217*7c478bd9Sstevel@tonic-gate 4218*7c478bd9Sstevel@tonic-gate /* mailer: assume about 19 bytes (max 10 byte mailer name) */ 4219*7c478bd9Sstevel@tonic-gate if (m != NULL) 4220*7c478bd9Sstevel@tonic-gate { 4221*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 4222*7c478bd9Sstevel@tonic-gate m->m_name); 4223*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4224*7c478bd9Sstevel@tonic-gate } 4225*7c478bd9Sstevel@tonic-gate 4226*7c478bd9Sstevel@tonic-gate /* pri: changes with each delivery attempt */ 4227*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), ", pri=%ld", 4228*7c478bd9Sstevel@tonic-gate e->e_msgpriority); 4229*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4230*7c478bd9Sstevel@tonic-gate 4231*7c478bd9Sstevel@tonic-gate /* relay: max 66 bytes for IPv4 addresses */ 4232*7c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 4233*7c478bd9Sstevel@tonic-gate { 4234*7c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 4235*7c478bd9Sstevel@tonic-gate 4236*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", relay=", 4237*7c478bd9Sstevel@tonic-gate shortenstring(mci->mci_host, 40)); 4238*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4239*7c478bd9Sstevel@tonic-gate 4240*7c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 4241*7c478bd9Sstevel@tonic-gate { 4242*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " [%s]", 4243*7c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 4244*7c478bd9Sstevel@tonic-gate } 4245*7c478bd9Sstevel@tonic-gate } 4246*7c478bd9Sstevel@tonic-gate else if (strcmp(status, "quarantined") == 0) 4247*7c478bd9Sstevel@tonic-gate { 4248*7c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 4249*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4250*7c478bd9Sstevel@tonic-gate ", quarantine=%s", 4251*7c478bd9Sstevel@tonic-gate shortenstring(e->e_quarmsg, 40)); 4252*7c478bd9Sstevel@tonic-gate } 4253*7c478bd9Sstevel@tonic-gate else if (strcmp(status, "queued") != 0) 4254*7c478bd9Sstevel@tonic-gate { 4255*7c478bd9Sstevel@tonic-gate p = macvalue('h', e); 4256*7c478bd9Sstevel@tonic-gate if (p != NULL && p[0] != '\0') 4257*7c478bd9Sstevel@tonic-gate { 4258*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4259*7c478bd9Sstevel@tonic-gate ", relay=%s", shortenstring(p, 40)); 4260*7c478bd9Sstevel@tonic-gate } 4261*7c478bd9Sstevel@tonic-gate } 4262*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4263*7c478bd9Sstevel@tonic-gate 4264*7c478bd9Sstevel@tonic-gate /* dsn */ 4265*7c478bd9Sstevel@tonic-gate if (dsn != NULL && *dsn != '\0') 4266*7c478bd9Sstevel@tonic-gate { 4267*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", dsn=", 4268*7c478bd9Sstevel@tonic-gate shortenstring(dsn, ENHSCLEN)); 4269*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4270*7c478bd9Sstevel@tonic-gate } 4271*7c478bd9Sstevel@tonic-gate 4272*7c478bd9Sstevel@tonic-gate #if _FFR_LOG_NTRIES 4273*7c478bd9Sstevel@tonic-gate /* ntries */ 4274*7c478bd9Sstevel@tonic-gate if (e->e_ntries >= 0) 4275*7c478bd9Sstevel@tonic-gate { 4276*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4277*7c478bd9Sstevel@tonic-gate ", ntries=%d", e->e_ntries + 1); 4278*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4279*7c478bd9Sstevel@tonic-gate } 4280*7c478bd9Sstevel@tonic-gate #endif /* _FFR_LOG_NTRIES */ 4281*7c478bd9Sstevel@tonic-gate 4282*7c478bd9Sstevel@tonic-gate # define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) 4283*7c478bd9Sstevel@tonic-gate # if (STATLEN) < 63 4284*7c478bd9Sstevel@tonic-gate # undef STATLEN 4285*7c478bd9Sstevel@tonic-gate # define STATLEN 63 4286*7c478bd9Sstevel@tonic-gate # endif /* (STATLEN) < 63 */ 4287*7c478bd9Sstevel@tonic-gate # if (STATLEN) > 203 4288*7c478bd9Sstevel@tonic-gate # undef STATLEN 4289*7c478bd9Sstevel@tonic-gate # define STATLEN 203 4290*7c478bd9Sstevel@tonic-gate # endif /* (STATLEN) > 203 */ 4291*7c478bd9Sstevel@tonic-gate 4292*7c478bd9Sstevel@tonic-gate /* stat: max 210 bytes */ 4293*7c478bd9Sstevel@tonic-gate if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) 4294*7c478bd9Sstevel@tonic-gate { 4295*7c478bd9Sstevel@tonic-gate /* desperation move -- truncate data */ 4296*7c478bd9Sstevel@tonic-gate bp = buf + sizeof buf - ((STATLEN) + 17); 4297*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, "...", SPACELEFT(buf, bp)); 4298*7c478bd9Sstevel@tonic-gate bp += 3; 4299*7c478bd9Sstevel@tonic-gate } 4300*7c478bd9Sstevel@tonic-gate 4301*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, ", stat=", SPACELEFT(buf, bp)); 4302*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4303*7c478bd9Sstevel@tonic-gate 4304*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(bp, shortenstring(status, STATLEN), 4305*7c478bd9Sstevel@tonic-gate SPACELEFT(buf, bp)); 4306*7c478bd9Sstevel@tonic-gate 4307*7c478bd9Sstevel@tonic-gate /* id, to: max 13 + TOBUFSIZE bytes */ 4308*7c478bd9Sstevel@tonic-gate l = SYSLOG_BUFSIZE - 100 - strlen(buf); 4309*7c478bd9Sstevel@tonic-gate if (l < 0) 4310*7c478bd9Sstevel@tonic-gate l = 0; 4311*7c478bd9Sstevel@tonic-gate p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 4312*7c478bd9Sstevel@tonic-gate while (strlen(p) >= l) 4313*7c478bd9Sstevel@tonic-gate { 4314*7c478bd9Sstevel@tonic-gate register char *q; 4315*7c478bd9Sstevel@tonic-gate 4316*7c478bd9Sstevel@tonic-gate for (q = p + l; q > p; q--) 4317*7c478bd9Sstevel@tonic-gate { 4318*7c478bd9Sstevel@tonic-gate if (*q == ',') 4319*7c478bd9Sstevel@tonic-gate break; 4320*7c478bd9Sstevel@tonic-gate } 4321*7c478bd9Sstevel@tonic-gate if (p == q) 4322*7c478bd9Sstevel@tonic-gate break; 4323*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]%s", 4324*7c478bd9Sstevel@tonic-gate (int) (++q - p), p, buf); 4325*7c478bd9Sstevel@tonic-gate p = q; 4326*7c478bd9Sstevel@tonic-gate } 4327*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s%s", l, p, buf); 4328*7c478bd9Sstevel@tonic-gate 4329*7c478bd9Sstevel@tonic-gate #else /* (SYSLOG_BUFSIZE) >= 256 */ 4330*7c478bd9Sstevel@tonic-gate 4331*7c478bd9Sstevel@tonic-gate l = SYSLOG_BUFSIZE - 85; 4332*7c478bd9Sstevel@tonic-gate if (l < 0) 4333*7c478bd9Sstevel@tonic-gate l = 0; 4334*7c478bd9Sstevel@tonic-gate p = e->e_to == NULL ? "NO-TO-LIST" : e->e_to; 4335*7c478bd9Sstevel@tonic-gate while (strlen(p) >= l) 4336*7c478bd9Sstevel@tonic-gate { 4337*7c478bd9Sstevel@tonic-gate register char *q; 4338*7c478bd9Sstevel@tonic-gate 4339*7c478bd9Sstevel@tonic-gate for (q = p + l; q > p; q--) 4340*7c478bd9Sstevel@tonic-gate { 4341*7c478bd9Sstevel@tonic-gate if (*q == ',') 4342*7c478bd9Sstevel@tonic-gate break; 4343*7c478bd9Sstevel@tonic-gate } 4344*7c478bd9Sstevel@tonic-gate if (p == q) 4345*7c478bd9Sstevel@tonic-gate break; 4346*7c478bd9Sstevel@tonic-gate 4347*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s [more]", 4348*7c478bd9Sstevel@tonic-gate (int) (++q - p), p); 4349*7c478bd9Sstevel@tonic-gate p = q; 4350*7c478bd9Sstevel@tonic-gate } 4351*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "to=%.*s", l, p); 4352*7c478bd9Sstevel@tonic-gate 4353*7c478bd9Sstevel@tonic-gate if (ctladdr != NULL) 4354*7c478bd9Sstevel@tonic-gate { 4355*7c478bd9Sstevel@tonic-gate bp = buf; 4356*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "ctladdr=", 4357*7c478bd9Sstevel@tonic-gate shortenstring(ctladdr->q_paddr, 83)); 4358*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4359*7c478bd9Sstevel@tonic-gate if (bitset(QGOODUID, ctladdr->q_flags)) 4360*7c478bd9Sstevel@tonic-gate { 4361*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", 4362*7c478bd9Sstevel@tonic-gate ctladdr->q_uid, ctladdr->q_gid); 4363*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4364*7c478bd9Sstevel@tonic-gate } 4365*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%s", buf); 4366*7c478bd9Sstevel@tonic-gate } 4367*7c478bd9Sstevel@tonic-gate bp = buf; 4368*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, "delay=", 4369*7c478bd9Sstevel@tonic-gate pintvl(now - e->e_ctime, true)); 4370*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4371*7c478bd9Sstevel@tonic-gate if (xstart != (time_t) 0) 4372*7c478bd9Sstevel@tonic-gate { 4373*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", xdelay=", 4374*7c478bd9Sstevel@tonic-gate pintvl(now - xstart, true)); 4375*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4376*7c478bd9Sstevel@tonic-gate } 4377*7c478bd9Sstevel@tonic-gate 4378*7c478bd9Sstevel@tonic-gate if (m != NULL) 4379*7c478bd9Sstevel@tonic-gate { 4380*7c478bd9Sstevel@tonic-gate (void) sm_strlcpyn(bp, SPACELEFT(buf, bp), 2, ", mailer=", 4381*7c478bd9Sstevel@tonic-gate m->m_name); 4382*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4383*7c478bd9Sstevel@tonic-gate } 4384*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 4385*7c478bd9Sstevel@tonic-gate 4386*7c478bd9Sstevel@tonic-gate buf[0] = '\0'; 4387*7c478bd9Sstevel@tonic-gate bp = buf; 4388*7c478bd9Sstevel@tonic-gate if (mci != NULL && mci->mci_host != NULL) 4389*7c478bd9Sstevel@tonic-gate { 4390*7c478bd9Sstevel@tonic-gate extern SOCKADDR CurHostAddr; 4391*7c478bd9Sstevel@tonic-gate 4392*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", 4393*7c478bd9Sstevel@tonic-gate mci->mci_host); 4394*7c478bd9Sstevel@tonic-gate bp += strlen(bp); 4395*7c478bd9Sstevel@tonic-gate 4396*7c478bd9Sstevel@tonic-gate if (CurHostAddr.sa.sa_family != 0) 4397*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4398*7c478bd9Sstevel@tonic-gate " [%.100s]", 4399*7c478bd9Sstevel@tonic-gate anynet_ntoa(&CurHostAddr)); 4400*7c478bd9Sstevel@tonic-gate } 4401*7c478bd9Sstevel@tonic-gate else if (strcmp(status, "quarantined") == 0) 4402*7c478bd9Sstevel@tonic-gate { 4403*7c478bd9Sstevel@tonic-gate if (e->e_quarmsg != NULL) 4404*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(bp, SPACELEFT(buf, bp), 4405*7c478bd9Sstevel@tonic-gate ", quarantine=%.100s", 4406*7c478bd9Sstevel@tonic-gate e->e_quarmsg); 4407*7c478bd9Sstevel@tonic-gate } 4408*7c478bd9Sstevel@tonic-gate else if (strcmp(status, "queued") != 0) 4409*7c478bd9Sstevel@tonic-gate { 4410*7c478bd9Sstevel@tonic-gate p = macvalue('h', e); 4411*7c478bd9Sstevel@tonic-gate if (p != NULL && p[0] != '\0') 4412*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, "relay=%.100s", p); 4413*7c478bd9Sstevel@tonic-gate } 4414*7c478bd9Sstevel@tonic-gate if (buf[0] != '\0') 4415*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); 4416*7c478bd9Sstevel@tonic-gate 4417*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(status, 63)); 4418*7c478bd9Sstevel@tonic-gate #endif /* (SYSLOG_BUFSIZE) >= 256 */ 4419*7c478bd9Sstevel@tonic-gate } 4420*7c478bd9Sstevel@tonic-gate /* 4421*7c478bd9Sstevel@tonic-gate ** PUTFROMLINE -- output a UNIX-style from line (or whatever) 4422*7c478bd9Sstevel@tonic-gate ** 4423*7c478bd9Sstevel@tonic-gate ** This can be made an arbitrary message separator by changing $l 4424*7c478bd9Sstevel@tonic-gate ** 4425*7c478bd9Sstevel@tonic-gate ** One of the ugliest hacks seen by human eyes is contained herein: 4426*7c478bd9Sstevel@tonic-gate ** UUCP wants those stupid "remote from <host>" lines. Why oh why 4427*7c478bd9Sstevel@tonic-gate ** does a well-meaning programmer such as myself have to deal with 4428*7c478bd9Sstevel@tonic-gate ** this kind of antique garbage???? 4429*7c478bd9Sstevel@tonic-gate ** 4430*7c478bd9Sstevel@tonic-gate ** Parameters: 4431*7c478bd9Sstevel@tonic-gate ** mci -- the connection information. 4432*7c478bd9Sstevel@tonic-gate ** e -- the envelope. 4433*7c478bd9Sstevel@tonic-gate ** 4434*7c478bd9Sstevel@tonic-gate ** Returns: 4435*7c478bd9Sstevel@tonic-gate ** none 4436*7c478bd9Sstevel@tonic-gate ** 4437*7c478bd9Sstevel@tonic-gate ** Side Effects: 4438*7c478bd9Sstevel@tonic-gate ** outputs some text to fp. 4439*7c478bd9Sstevel@tonic-gate */ 4440*7c478bd9Sstevel@tonic-gate 4441*7c478bd9Sstevel@tonic-gate void 4442*7c478bd9Sstevel@tonic-gate putfromline(mci, e) 4443*7c478bd9Sstevel@tonic-gate register MCI *mci; 4444*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 4445*7c478bd9Sstevel@tonic-gate { 4446*7c478bd9Sstevel@tonic-gate char *template = UnixFromLine; 4447*7c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 4448*7c478bd9Sstevel@tonic-gate char xbuf[MAXLINE]; 4449*7c478bd9Sstevel@tonic-gate 4450*7c478bd9Sstevel@tonic-gate if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) 4451*7c478bd9Sstevel@tonic-gate return; 4452*7c478bd9Sstevel@tonic-gate 4453*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 4454*7c478bd9Sstevel@tonic-gate 4455*7c478bd9Sstevel@tonic-gate if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) 4456*7c478bd9Sstevel@tonic-gate { 4457*7c478bd9Sstevel@tonic-gate char *bang; 4458*7c478bd9Sstevel@tonic-gate 4459*7c478bd9Sstevel@tonic-gate expand("\201g", buf, sizeof buf, e); 4460*7c478bd9Sstevel@tonic-gate bang = strchr(buf, '!'); 4461*7c478bd9Sstevel@tonic-gate if (bang == NULL) 4462*7c478bd9Sstevel@tonic-gate { 4463*7c478bd9Sstevel@tonic-gate char *at; 4464*7c478bd9Sstevel@tonic-gate char hname[MAXNAME]; 4465*7c478bd9Sstevel@tonic-gate 4466*7c478bd9Sstevel@tonic-gate /* 4467*7c478bd9Sstevel@tonic-gate ** If we can construct a UUCP path, do so 4468*7c478bd9Sstevel@tonic-gate */ 4469*7c478bd9Sstevel@tonic-gate 4470*7c478bd9Sstevel@tonic-gate at = strrchr(buf, '@'); 4471*7c478bd9Sstevel@tonic-gate if (at == NULL) 4472*7c478bd9Sstevel@tonic-gate { 4473*7c478bd9Sstevel@tonic-gate expand("\201k", hname, sizeof hname, e); 4474*7c478bd9Sstevel@tonic-gate at = hname; 4475*7c478bd9Sstevel@tonic-gate } 4476*7c478bd9Sstevel@tonic-gate else 4477*7c478bd9Sstevel@tonic-gate *at++ = '\0'; 4478*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(xbuf, sizeof xbuf, 4479*7c478bd9Sstevel@tonic-gate "From %.800s \201d remote from %.100s\n", 4480*7c478bd9Sstevel@tonic-gate buf, at); 4481*7c478bd9Sstevel@tonic-gate } 4482*7c478bd9Sstevel@tonic-gate else 4483*7c478bd9Sstevel@tonic-gate { 4484*7c478bd9Sstevel@tonic-gate *bang++ = '\0'; 4485*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(xbuf, sizeof xbuf, 4486*7c478bd9Sstevel@tonic-gate "From %.800s \201d remote from %.100s\n", 4487*7c478bd9Sstevel@tonic-gate bang, buf); 4488*7c478bd9Sstevel@tonic-gate template = xbuf; 4489*7c478bd9Sstevel@tonic-gate } 4490*7c478bd9Sstevel@tonic-gate } 4491*7c478bd9Sstevel@tonic-gate expand(template, buf, sizeof buf, e); 4492*7c478bd9Sstevel@tonic-gate putxline(buf, strlen(buf), mci, PXLF_HEADER); 4493*7c478bd9Sstevel@tonic-gate } 4494*7c478bd9Sstevel@tonic-gate /* 4495*7c478bd9Sstevel@tonic-gate ** PUTBODY -- put the body of a message. 4496*7c478bd9Sstevel@tonic-gate ** 4497*7c478bd9Sstevel@tonic-gate ** Parameters: 4498*7c478bd9Sstevel@tonic-gate ** mci -- the connection information. 4499*7c478bd9Sstevel@tonic-gate ** e -- the envelope to put out. 4500*7c478bd9Sstevel@tonic-gate ** separator -- if non-NULL, a message separator that must 4501*7c478bd9Sstevel@tonic-gate ** not be permitted in the resulting message. 4502*7c478bd9Sstevel@tonic-gate ** 4503*7c478bd9Sstevel@tonic-gate ** Returns: 4504*7c478bd9Sstevel@tonic-gate ** none. 4505*7c478bd9Sstevel@tonic-gate ** 4506*7c478bd9Sstevel@tonic-gate ** Side Effects: 4507*7c478bd9Sstevel@tonic-gate ** The message is written onto fp. 4508*7c478bd9Sstevel@tonic-gate */ 4509*7c478bd9Sstevel@tonic-gate 4510*7c478bd9Sstevel@tonic-gate /* values for output state variable */ 4511*7c478bd9Sstevel@tonic-gate #define OS_HEAD 0 /* at beginning of line */ 4512*7c478bd9Sstevel@tonic-gate #define OS_CR 1 /* read a carriage return */ 4513*7c478bd9Sstevel@tonic-gate #define OS_INLINE 2 /* putting rest of line */ 4514*7c478bd9Sstevel@tonic-gate 4515*7c478bd9Sstevel@tonic-gate void 4516*7c478bd9Sstevel@tonic-gate putbody(mci, e, separator) 4517*7c478bd9Sstevel@tonic-gate register MCI *mci; 4518*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 4519*7c478bd9Sstevel@tonic-gate char *separator; 4520*7c478bd9Sstevel@tonic-gate { 4521*7c478bd9Sstevel@tonic-gate bool dead = false; 4522*7c478bd9Sstevel@tonic-gate char buf[MAXLINE]; 4523*7c478bd9Sstevel@tonic-gate #if MIME8TO7 4524*7c478bd9Sstevel@tonic-gate char *boundaries[MAXMIMENESTING + 1]; 4525*7c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */ 4526*7c478bd9Sstevel@tonic-gate 4527*7c478bd9Sstevel@tonic-gate /* 4528*7c478bd9Sstevel@tonic-gate ** Output the body of the message 4529*7c478bd9Sstevel@tonic-gate */ 4530*7c478bd9Sstevel@tonic-gate 4531*7c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 4532*7c478bd9Sstevel@tonic-gate { 4533*7c478bd9Sstevel@tonic-gate char *df = queuename(e, DATAFL_LETTER); 4534*7c478bd9Sstevel@tonic-gate 4535*7c478bd9Sstevel@tonic-gate e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 4536*7c478bd9Sstevel@tonic-gate SM_IO_RDONLY_B, NULL); 4537*7c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 4538*7c478bd9Sstevel@tonic-gate { 4539*7c478bd9Sstevel@tonic-gate char *msg = "!putbody: Cannot open %s for %s from %s"; 4540*7c478bd9Sstevel@tonic-gate 4541*7c478bd9Sstevel@tonic-gate if (errno == ENOENT) 4542*7c478bd9Sstevel@tonic-gate msg++; 4543*7c478bd9Sstevel@tonic-gate syserr(msg, df, e->e_to, e->e_from.q_paddr); 4544*7c478bd9Sstevel@tonic-gate } 4545*7c478bd9Sstevel@tonic-gate 4546*7c478bd9Sstevel@tonic-gate } 4547*7c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 4548*7c478bd9Sstevel@tonic-gate { 4549*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_INHEADER, mci->mci_flags)) 4550*7c478bd9Sstevel@tonic-gate { 4551*7c478bd9Sstevel@tonic-gate putline("", mci); 4552*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_INHEADER; 4553*7c478bd9Sstevel@tonic-gate } 4554*7c478bd9Sstevel@tonic-gate putline("<<< No Message Collected >>>", mci); 4555*7c478bd9Sstevel@tonic-gate goto endofmessage; 4556*7c478bd9Sstevel@tonic-gate } 4557*7c478bd9Sstevel@tonic-gate 4558*7c478bd9Sstevel@tonic-gate if (e->e_dfino == (ino_t) 0) 4559*7c478bd9Sstevel@tonic-gate { 4560*7c478bd9Sstevel@tonic-gate struct stat stbuf; 4561*7c478bd9Sstevel@tonic-gate 4562*7c478bd9Sstevel@tonic-gate if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &stbuf) 4563*7c478bd9Sstevel@tonic-gate < 0) 4564*7c478bd9Sstevel@tonic-gate e->e_dfino = -1; 4565*7c478bd9Sstevel@tonic-gate else 4566*7c478bd9Sstevel@tonic-gate { 4567*7c478bd9Sstevel@tonic-gate e->e_dfdev = stbuf.st_dev; 4568*7c478bd9Sstevel@tonic-gate e->e_dfino = stbuf.st_ino; 4569*7c478bd9Sstevel@tonic-gate } 4570*7c478bd9Sstevel@tonic-gate } 4571*7c478bd9Sstevel@tonic-gate 4572*7c478bd9Sstevel@tonic-gate /* paranoia: the data file should always be in a rewound state */ 4573*7c478bd9Sstevel@tonic-gate (void) bfrewind(e->e_dfp); 4574*7c478bd9Sstevel@tonic-gate 4575*7c478bd9Sstevel@tonic-gate #if MIME8TO7 4576*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_CVT8TO7, mci->mci_flags)) 4577*7c478bd9Sstevel@tonic-gate { 4578*7c478bd9Sstevel@tonic-gate /* 4579*7c478bd9Sstevel@tonic-gate ** Do 8 to 7 bit MIME conversion. 4580*7c478bd9Sstevel@tonic-gate */ 4581*7c478bd9Sstevel@tonic-gate 4582*7c478bd9Sstevel@tonic-gate /* make sure it looks like a MIME message */ 4583*7c478bd9Sstevel@tonic-gate if (hvalue("MIME-Version", e->e_header) == NULL) 4584*7c478bd9Sstevel@tonic-gate putline("MIME-Version: 1.0", mci); 4585*7c478bd9Sstevel@tonic-gate 4586*7c478bd9Sstevel@tonic-gate if (hvalue("Content-Type", e->e_header) == NULL) 4587*7c478bd9Sstevel@tonic-gate { 4588*7c478bd9Sstevel@tonic-gate (void) sm_snprintf(buf, sizeof buf, 4589*7c478bd9Sstevel@tonic-gate "Content-Type: text/plain; charset=%s", 4590*7c478bd9Sstevel@tonic-gate defcharset(e)); 4591*7c478bd9Sstevel@tonic-gate putline(buf, mci); 4592*7c478bd9Sstevel@tonic-gate } 4593*7c478bd9Sstevel@tonic-gate 4594*7c478bd9Sstevel@tonic-gate /* now do the hard work */ 4595*7c478bd9Sstevel@tonic-gate boundaries[0] = NULL; 4596*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 4597*7c478bd9Sstevel@tonic-gate (void) mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); 4598*7c478bd9Sstevel@tonic-gate } 4599*7c478bd9Sstevel@tonic-gate # if MIME7TO8 4600*7c478bd9Sstevel@tonic-gate else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) 4601*7c478bd9Sstevel@tonic-gate { 4602*7c478bd9Sstevel@tonic-gate (void) mime7to8(mci, e->e_header, e); 4603*7c478bd9Sstevel@tonic-gate } 4604*7c478bd9Sstevel@tonic-gate # endif /* MIME7TO8 */ 4605*7c478bd9Sstevel@tonic-gate else if (MaxMimeHeaderLength > 0 || MaxMimeFieldLength > 0) 4606*7c478bd9Sstevel@tonic-gate { 4607*7c478bd9Sstevel@tonic-gate bool oldsuprerrs = SuprErrs; 4608*7c478bd9Sstevel@tonic-gate 4609*7c478bd9Sstevel@tonic-gate /* Use mime8to7 to check multipart for MIME header overflows */ 4610*7c478bd9Sstevel@tonic-gate boundaries[0] = NULL; 4611*7c478bd9Sstevel@tonic-gate mci->mci_flags |= MCIF_INHEADER; 4612*7c478bd9Sstevel@tonic-gate 4613*7c478bd9Sstevel@tonic-gate /* 4614*7c478bd9Sstevel@tonic-gate ** If EF_DONT_MIME is set, we have a broken MIME message 4615*7c478bd9Sstevel@tonic-gate ** and don't want to generate a new bounce message whose 4616*7c478bd9Sstevel@tonic-gate ** body propagates the broken MIME. We can't just not call 4617*7c478bd9Sstevel@tonic-gate ** mime8to7() as is done above since we need the security 4618*7c478bd9Sstevel@tonic-gate ** checks. The best we can do is suppress the errors. 4619*7c478bd9Sstevel@tonic-gate */ 4620*7c478bd9Sstevel@tonic-gate 4621*7c478bd9Sstevel@tonic-gate if (bitset(EF_DONT_MIME, e->e_flags)) 4622*7c478bd9Sstevel@tonic-gate SuprErrs = true; 4623*7c478bd9Sstevel@tonic-gate 4624*7c478bd9Sstevel@tonic-gate (void) mime8to7(mci, e->e_header, e, boundaries, 4625*7c478bd9Sstevel@tonic-gate M87F_OUTER|M87F_NO8TO7); 4626*7c478bd9Sstevel@tonic-gate 4627*7c478bd9Sstevel@tonic-gate /* restore SuprErrs */ 4628*7c478bd9Sstevel@tonic-gate SuprErrs = oldsuprerrs; 4629*7c478bd9Sstevel@tonic-gate } 4630*7c478bd9Sstevel@tonic-gate else 4631*7c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */ 4632*7c478bd9Sstevel@tonic-gate { 4633*7c478bd9Sstevel@tonic-gate int ostate; 4634*7c478bd9Sstevel@tonic-gate register char *bp; 4635*7c478bd9Sstevel@tonic-gate register char *pbp; 4636*7c478bd9Sstevel@tonic-gate register int c; 4637*7c478bd9Sstevel@tonic-gate register char *xp; 4638*7c478bd9Sstevel@tonic-gate int padc; 4639*7c478bd9Sstevel@tonic-gate char *buflim; 4640*7c478bd9Sstevel@tonic-gate int pos = 0; 4641*7c478bd9Sstevel@tonic-gate char peekbuf[12]; 4642*7c478bd9Sstevel@tonic-gate 4643*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_INHEADER, mci->mci_flags)) 4644*7c478bd9Sstevel@tonic-gate { 4645*7c478bd9Sstevel@tonic-gate putline("", mci); 4646*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_INHEADER; 4647*7c478bd9Sstevel@tonic-gate } 4648*7c478bd9Sstevel@tonic-gate 4649*7c478bd9Sstevel@tonic-gate /* determine end of buffer; allow for short mailer lines */ 4650*7c478bd9Sstevel@tonic-gate buflim = &buf[sizeof buf - 1]; 4651*7c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_linelimit > 0 && 4652*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_linelimit < sizeof buf - 1) 4653*7c478bd9Sstevel@tonic-gate buflim = &buf[mci->mci_mailer->m_linelimit - 1]; 4654*7c478bd9Sstevel@tonic-gate 4655*7c478bd9Sstevel@tonic-gate /* copy temp file to output with mapping */ 4656*7c478bd9Sstevel@tonic-gate ostate = OS_HEAD; 4657*7c478bd9Sstevel@tonic-gate bp = buf; 4658*7c478bd9Sstevel@tonic-gate pbp = peekbuf; 4659*7c478bd9Sstevel@tonic-gate while (!sm_io_error(mci->mci_out) && !dead) 4660*7c478bd9Sstevel@tonic-gate { 4661*7c478bd9Sstevel@tonic-gate if (pbp > peekbuf) 4662*7c478bd9Sstevel@tonic-gate c = *--pbp; 4663*7c478bd9Sstevel@tonic-gate else if ((c = sm_io_getc(e->e_dfp, SM_TIME_DEFAULT)) 4664*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4665*7c478bd9Sstevel@tonic-gate break; 4666*7c478bd9Sstevel@tonic-gate if (bitset(MCIF_7BIT, mci->mci_flags)) 4667*7c478bd9Sstevel@tonic-gate c &= 0x7f; 4668*7c478bd9Sstevel@tonic-gate switch (ostate) 4669*7c478bd9Sstevel@tonic-gate { 4670*7c478bd9Sstevel@tonic-gate case OS_HEAD: 4671*7c478bd9Sstevel@tonic-gate if (c == '\0' && 4672*7c478bd9Sstevel@tonic-gate bitnset(M_NONULLS, 4673*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_flags)) 4674*7c478bd9Sstevel@tonic-gate break; 4675*7c478bd9Sstevel@tonic-gate if (c != '\r' && c != '\n' && bp < buflim) 4676*7c478bd9Sstevel@tonic-gate { 4677*7c478bd9Sstevel@tonic-gate *bp++ = c; 4678*7c478bd9Sstevel@tonic-gate break; 4679*7c478bd9Sstevel@tonic-gate } 4680*7c478bd9Sstevel@tonic-gate 4681*7c478bd9Sstevel@tonic-gate /* check beginning of line for special cases */ 4682*7c478bd9Sstevel@tonic-gate *bp = '\0'; 4683*7c478bd9Sstevel@tonic-gate pos = 0; 4684*7c478bd9Sstevel@tonic-gate padc = SM_IO_EOF; 4685*7c478bd9Sstevel@tonic-gate if (buf[0] == 'F' && 4686*7c478bd9Sstevel@tonic-gate bitnset(M_ESCFROM, mci->mci_mailer->m_flags) 4687*7c478bd9Sstevel@tonic-gate && strncmp(buf, "From ", 5) == 0) 4688*7c478bd9Sstevel@tonic-gate { 4689*7c478bd9Sstevel@tonic-gate padc = '>'; 4690*7c478bd9Sstevel@tonic-gate } 4691*7c478bd9Sstevel@tonic-gate if (buf[0] == '-' && buf[1] == '-' && 4692*7c478bd9Sstevel@tonic-gate separator != NULL) 4693*7c478bd9Sstevel@tonic-gate { 4694*7c478bd9Sstevel@tonic-gate /* possible separator */ 4695*7c478bd9Sstevel@tonic-gate int sl = strlen(separator); 4696*7c478bd9Sstevel@tonic-gate 4697*7c478bd9Sstevel@tonic-gate if (strncmp(&buf[2], separator, sl) 4698*7c478bd9Sstevel@tonic-gate == 0) 4699*7c478bd9Sstevel@tonic-gate padc = ' '; 4700*7c478bd9Sstevel@tonic-gate } 4701*7c478bd9Sstevel@tonic-gate if (buf[0] == '.' && 4702*7c478bd9Sstevel@tonic-gate bitnset(M_XDOT, mci->mci_mailer->m_flags)) 4703*7c478bd9Sstevel@tonic-gate { 4704*7c478bd9Sstevel@tonic-gate padc = '.'; 4705*7c478bd9Sstevel@tonic-gate } 4706*7c478bd9Sstevel@tonic-gate 4707*7c478bd9Sstevel@tonic-gate /* now copy out saved line */ 4708*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4709*7c478bd9Sstevel@tonic-gate { 4710*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 4711*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4712*7c478bd9Sstevel@tonic-gate "%05d >>> ", 4713*7c478bd9Sstevel@tonic-gate (int) CurrentPid); 4714*7c478bd9Sstevel@tonic-gate if (padc != SM_IO_EOF) 4715*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 4716*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4717*7c478bd9Sstevel@tonic-gate padc); 4718*7c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 4719*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 4720*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4721*7c478bd9Sstevel@tonic-gate (unsigned char) *xp); 4722*7c478bd9Sstevel@tonic-gate if (c == '\n') 4723*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 4724*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4725*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4726*7c478bd9Sstevel@tonic-gate } 4727*7c478bd9Sstevel@tonic-gate if (padc != SM_IO_EOF) 4728*7c478bd9Sstevel@tonic-gate { 4729*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 4730*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, padc) 4731*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4732*7c478bd9Sstevel@tonic-gate { 4733*7c478bd9Sstevel@tonic-gate dead = true; 4734*7c478bd9Sstevel@tonic-gate continue; 4735*7c478bd9Sstevel@tonic-gate } 4736*7c478bd9Sstevel@tonic-gate else 4737*7c478bd9Sstevel@tonic-gate { 4738*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4739*7c478bd9Sstevel@tonic-gate DataProgress = true; 4740*7c478bd9Sstevel@tonic-gate } 4741*7c478bd9Sstevel@tonic-gate pos++; 4742*7c478bd9Sstevel@tonic-gate } 4743*7c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 4744*7c478bd9Sstevel@tonic-gate { 4745*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 4746*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4747*7c478bd9Sstevel@tonic-gate (unsigned char) *xp) 4748*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4749*7c478bd9Sstevel@tonic-gate { 4750*7c478bd9Sstevel@tonic-gate dead = true; 4751*7c478bd9Sstevel@tonic-gate break; 4752*7c478bd9Sstevel@tonic-gate } 4753*7c478bd9Sstevel@tonic-gate else 4754*7c478bd9Sstevel@tonic-gate { 4755*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4756*7c478bd9Sstevel@tonic-gate DataProgress = true; 4757*7c478bd9Sstevel@tonic-gate } 4758*7c478bd9Sstevel@tonic-gate } 4759*7c478bd9Sstevel@tonic-gate if (dead) 4760*7c478bd9Sstevel@tonic-gate continue; 4761*7c478bd9Sstevel@tonic-gate if (c == '\n') 4762*7c478bd9Sstevel@tonic-gate { 4763*7c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 4764*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4765*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 4766*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4767*7c478bd9Sstevel@tonic-gate break; 4768*7c478bd9Sstevel@tonic-gate else 4769*7c478bd9Sstevel@tonic-gate { 4770*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4771*7c478bd9Sstevel@tonic-gate DataProgress = true; 4772*7c478bd9Sstevel@tonic-gate } 4773*7c478bd9Sstevel@tonic-gate pos = 0; 4774*7c478bd9Sstevel@tonic-gate } 4775*7c478bd9Sstevel@tonic-gate else 4776*7c478bd9Sstevel@tonic-gate { 4777*7c478bd9Sstevel@tonic-gate pos += bp - buf; 4778*7c478bd9Sstevel@tonic-gate if (c != '\r') 4779*7c478bd9Sstevel@tonic-gate { 4780*7c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 4781*7c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 4782*7c478bd9Sstevel@tonic-gate *pbp++ = c; 4783*7c478bd9Sstevel@tonic-gate } 4784*7c478bd9Sstevel@tonic-gate } 4785*7c478bd9Sstevel@tonic-gate 4786*7c478bd9Sstevel@tonic-gate bp = buf; 4787*7c478bd9Sstevel@tonic-gate 4788*7c478bd9Sstevel@tonic-gate /* determine next state */ 4789*7c478bd9Sstevel@tonic-gate if (c == '\n') 4790*7c478bd9Sstevel@tonic-gate ostate = OS_HEAD; 4791*7c478bd9Sstevel@tonic-gate else if (c == '\r') 4792*7c478bd9Sstevel@tonic-gate ostate = OS_CR; 4793*7c478bd9Sstevel@tonic-gate else 4794*7c478bd9Sstevel@tonic-gate ostate = OS_INLINE; 4795*7c478bd9Sstevel@tonic-gate continue; 4796*7c478bd9Sstevel@tonic-gate 4797*7c478bd9Sstevel@tonic-gate case OS_CR: 4798*7c478bd9Sstevel@tonic-gate if (c == '\n') 4799*7c478bd9Sstevel@tonic-gate { 4800*7c478bd9Sstevel@tonic-gate /* got CRLF */ 4801*7c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 4802*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4803*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 4804*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4805*7c478bd9Sstevel@tonic-gate continue; 4806*7c478bd9Sstevel@tonic-gate else 4807*7c478bd9Sstevel@tonic-gate { 4808*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4809*7c478bd9Sstevel@tonic-gate DataProgress = true; 4810*7c478bd9Sstevel@tonic-gate } 4811*7c478bd9Sstevel@tonic-gate 4812*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4813*7c478bd9Sstevel@tonic-gate { 4814*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 4815*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4816*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4817*7c478bd9Sstevel@tonic-gate } 4818*7c478bd9Sstevel@tonic-gate ostate = OS_HEAD; 4819*7c478bd9Sstevel@tonic-gate continue; 4820*7c478bd9Sstevel@tonic-gate } 4821*7c478bd9Sstevel@tonic-gate 4822*7c478bd9Sstevel@tonic-gate /* had a naked carriage return */ 4823*7c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + sizeof(peekbuf)); 4824*7c478bd9Sstevel@tonic-gate *pbp++ = c; 4825*7c478bd9Sstevel@tonic-gate c = '\r'; 4826*7c478bd9Sstevel@tonic-gate ostate = OS_INLINE; 4827*7c478bd9Sstevel@tonic-gate goto putch; 4828*7c478bd9Sstevel@tonic-gate 4829*7c478bd9Sstevel@tonic-gate case OS_INLINE: 4830*7c478bd9Sstevel@tonic-gate if (c == '\r') 4831*7c478bd9Sstevel@tonic-gate { 4832*7c478bd9Sstevel@tonic-gate ostate = OS_CR; 4833*7c478bd9Sstevel@tonic-gate continue; 4834*7c478bd9Sstevel@tonic-gate } 4835*7c478bd9Sstevel@tonic-gate if (c == '\0' && 4836*7c478bd9Sstevel@tonic-gate bitnset(M_NONULLS, 4837*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_flags)) 4838*7c478bd9Sstevel@tonic-gate break; 4839*7c478bd9Sstevel@tonic-gate putch: 4840*7c478bd9Sstevel@tonic-gate if (mci->mci_mailer->m_linelimit > 0 && 4841*7c478bd9Sstevel@tonic-gate pos >= mci->mci_mailer->m_linelimit - 1 && 4842*7c478bd9Sstevel@tonic-gate c != '\n') 4843*7c478bd9Sstevel@tonic-gate { 4844*7c478bd9Sstevel@tonic-gate int d; 4845*7c478bd9Sstevel@tonic-gate 4846*7c478bd9Sstevel@tonic-gate /* check next character for EOL */ 4847*7c478bd9Sstevel@tonic-gate if (pbp > peekbuf) 4848*7c478bd9Sstevel@tonic-gate d = *(pbp - 1); 4849*7c478bd9Sstevel@tonic-gate else if ((d = sm_io_getc(e->e_dfp, 4850*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT)) 4851*7c478bd9Sstevel@tonic-gate != SM_IO_EOF) 4852*7c478bd9Sstevel@tonic-gate { 4853*7c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 4854*7c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 4855*7c478bd9Sstevel@tonic-gate *pbp++ = d; 4856*7c478bd9Sstevel@tonic-gate } 4857*7c478bd9Sstevel@tonic-gate 4858*7c478bd9Sstevel@tonic-gate if (d == '\n' || d == SM_IO_EOF) 4859*7c478bd9Sstevel@tonic-gate { 4860*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4861*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 4862*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4863*7c478bd9Sstevel@tonic-gate (unsigned char) c); 4864*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 4865*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4866*7c478bd9Sstevel@tonic-gate (unsigned char) c) 4867*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4868*7c478bd9Sstevel@tonic-gate { 4869*7c478bd9Sstevel@tonic-gate dead = true; 4870*7c478bd9Sstevel@tonic-gate continue; 4871*7c478bd9Sstevel@tonic-gate } 4872*7c478bd9Sstevel@tonic-gate else 4873*7c478bd9Sstevel@tonic-gate { 4874*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4875*7c478bd9Sstevel@tonic-gate DataProgress = true; 4876*7c478bd9Sstevel@tonic-gate } 4877*7c478bd9Sstevel@tonic-gate pos++; 4878*7c478bd9Sstevel@tonic-gate continue; 4879*7c478bd9Sstevel@tonic-gate } 4880*7c478bd9Sstevel@tonic-gate 4881*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 4882*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, '!') 4883*7c478bd9Sstevel@tonic-gate == SM_IO_EOF || 4884*7c478bd9Sstevel@tonic-gate sm_io_fputs(mci->mci_out, 4885*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4886*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 4887*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4888*7c478bd9Sstevel@tonic-gate { 4889*7c478bd9Sstevel@tonic-gate dead = true; 4890*7c478bd9Sstevel@tonic-gate continue; 4891*7c478bd9Sstevel@tonic-gate } 4892*7c478bd9Sstevel@tonic-gate else 4893*7c478bd9Sstevel@tonic-gate { 4894*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4895*7c478bd9Sstevel@tonic-gate DataProgress = true; 4896*7c478bd9Sstevel@tonic-gate } 4897*7c478bd9Sstevel@tonic-gate 4898*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4899*7c478bd9Sstevel@tonic-gate { 4900*7c478bd9Sstevel@tonic-gate (void) sm_io_fprintf(TrafficLogFile, 4901*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4902*7c478bd9Sstevel@tonic-gate "!%s", 4903*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4904*7c478bd9Sstevel@tonic-gate } 4905*7c478bd9Sstevel@tonic-gate ostate = OS_HEAD; 4906*7c478bd9Sstevel@tonic-gate SM_ASSERT(pbp < peekbuf + 4907*7c478bd9Sstevel@tonic-gate sizeof(peekbuf)); 4908*7c478bd9Sstevel@tonic-gate *pbp++ = c; 4909*7c478bd9Sstevel@tonic-gate continue; 4910*7c478bd9Sstevel@tonic-gate } 4911*7c478bd9Sstevel@tonic-gate if (c == '\n') 4912*7c478bd9Sstevel@tonic-gate { 4913*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4914*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 4915*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4916*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4917*7c478bd9Sstevel@tonic-gate if (sm_io_fputs(mci->mci_out, 4918*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4919*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol) 4920*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4921*7c478bd9Sstevel@tonic-gate continue; 4922*7c478bd9Sstevel@tonic-gate else 4923*7c478bd9Sstevel@tonic-gate { 4924*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4925*7c478bd9Sstevel@tonic-gate DataProgress = true; 4926*7c478bd9Sstevel@tonic-gate } 4927*7c478bd9Sstevel@tonic-gate pos = 0; 4928*7c478bd9Sstevel@tonic-gate ostate = OS_HEAD; 4929*7c478bd9Sstevel@tonic-gate } 4930*7c478bd9Sstevel@tonic-gate else 4931*7c478bd9Sstevel@tonic-gate { 4932*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4933*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 4934*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4935*7c478bd9Sstevel@tonic-gate (unsigned char) c); 4936*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, 4937*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4938*7c478bd9Sstevel@tonic-gate (unsigned char) c) 4939*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4940*7c478bd9Sstevel@tonic-gate { 4941*7c478bd9Sstevel@tonic-gate dead = true; 4942*7c478bd9Sstevel@tonic-gate continue; 4943*7c478bd9Sstevel@tonic-gate } 4944*7c478bd9Sstevel@tonic-gate else 4945*7c478bd9Sstevel@tonic-gate { 4946*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4947*7c478bd9Sstevel@tonic-gate DataProgress = true; 4948*7c478bd9Sstevel@tonic-gate } 4949*7c478bd9Sstevel@tonic-gate pos++; 4950*7c478bd9Sstevel@tonic-gate ostate = OS_INLINE; 4951*7c478bd9Sstevel@tonic-gate } 4952*7c478bd9Sstevel@tonic-gate break; 4953*7c478bd9Sstevel@tonic-gate } 4954*7c478bd9Sstevel@tonic-gate } 4955*7c478bd9Sstevel@tonic-gate 4956*7c478bd9Sstevel@tonic-gate /* make sure we are at the beginning of a line */ 4957*7c478bd9Sstevel@tonic-gate if (bp > buf) 4958*7c478bd9Sstevel@tonic-gate { 4959*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4960*7c478bd9Sstevel@tonic-gate { 4961*7c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 4962*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(TrafficLogFile, 4963*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4964*7c478bd9Sstevel@tonic-gate (unsigned char) *xp); 4965*7c478bd9Sstevel@tonic-gate } 4966*7c478bd9Sstevel@tonic-gate for (xp = buf; xp < bp; xp++) 4967*7c478bd9Sstevel@tonic-gate { 4968*7c478bd9Sstevel@tonic-gate if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, 4969*7c478bd9Sstevel@tonic-gate (unsigned char) *xp) 4970*7c478bd9Sstevel@tonic-gate == SM_IO_EOF) 4971*7c478bd9Sstevel@tonic-gate { 4972*7c478bd9Sstevel@tonic-gate dead = true; 4973*7c478bd9Sstevel@tonic-gate break; 4974*7c478bd9Sstevel@tonic-gate } 4975*7c478bd9Sstevel@tonic-gate else 4976*7c478bd9Sstevel@tonic-gate { 4977*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4978*7c478bd9Sstevel@tonic-gate DataProgress = true; 4979*7c478bd9Sstevel@tonic-gate } 4980*7c478bd9Sstevel@tonic-gate } 4981*7c478bd9Sstevel@tonic-gate pos += bp - buf; 4982*7c478bd9Sstevel@tonic-gate } 4983*7c478bd9Sstevel@tonic-gate if (!dead && pos > 0) 4984*7c478bd9Sstevel@tonic-gate { 4985*7c478bd9Sstevel@tonic-gate if (TrafficLogFile != NULL) 4986*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(TrafficLogFile, 4987*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 4988*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4989*7c478bd9Sstevel@tonic-gate (void) sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT, 4990*7c478bd9Sstevel@tonic-gate mci->mci_mailer->m_eol); 4991*7c478bd9Sstevel@tonic-gate 4992*7c478bd9Sstevel@tonic-gate /* record progress for DATA timeout */ 4993*7c478bd9Sstevel@tonic-gate DataProgress = true; 4994*7c478bd9Sstevel@tonic-gate } 4995*7c478bd9Sstevel@tonic-gate } 4996*7c478bd9Sstevel@tonic-gate 4997*7c478bd9Sstevel@tonic-gate if (sm_io_error(e->e_dfp)) 4998*7c478bd9Sstevel@tonic-gate { 4999*7c478bd9Sstevel@tonic-gate syserr("putbody: %s/%cf%s: read error", 5000*7c478bd9Sstevel@tonic-gate qid_printqueue(e->e_dfqgrp, e->e_dfqdir), 5001*7c478bd9Sstevel@tonic-gate DATAFL_LETTER, e->e_id); 5002*7c478bd9Sstevel@tonic-gate ExitStat = EX_IOERR; 5003*7c478bd9Sstevel@tonic-gate } 5004*7c478bd9Sstevel@tonic-gate 5005*7c478bd9Sstevel@tonic-gate endofmessage: 5006*7c478bd9Sstevel@tonic-gate /* 5007*7c478bd9Sstevel@tonic-gate ** Since mailfile() uses e_dfp in a child process, 5008*7c478bd9Sstevel@tonic-gate ** the file offset in the stdio library for the 5009*7c478bd9Sstevel@tonic-gate ** parent process will not agree with the in-kernel 5010*7c478bd9Sstevel@tonic-gate ** file offset since the file descriptor is shared 5011*7c478bd9Sstevel@tonic-gate ** between the processes. Therefore, it is vital 5012*7c478bd9Sstevel@tonic-gate ** that the file always be rewound. This forces the 5013*7c478bd9Sstevel@tonic-gate ** kernel offset (lseek) and stdio library (ftell) 5014*7c478bd9Sstevel@tonic-gate ** offset to match. 5015*7c478bd9Sstevel@tonic-gate */ 5016*7c478bd9Sstevel@tonic-gate 5017*7c478bd9Sstevel@tonic-gate if (e->e_dfp != NULL) 5018*7c478bd9Sstevel@tonic-gate (void) bfrewind(e->e_dfp); 5019*7c478bd9Sstevel@tonic-gate 5020*7c478bd9Sstevel@tonic-gate /* some mailers want extra blank line at end of message */ 5021*7c478bd9Sstevel@tonic-gate if (!dead && bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && 5022*7c478bd9Sstevel@tonic-gate buf[0] != '\0' && buf[0] != '\n') 5023*7c478bd9Sstevel@tonic-gate putline("", mci); 5024*7c478bd9Sstevel@tonic-gate 5025*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT); 5026*7c478bd9Sstevel@tonic-gate if (sm_io_error(mci->mci_out) && errno != EPIPE) 5027*7c478bd9Sstevel@tonic-gate { 5028*7c478bd9Sstevel@tonic-gate syserr("putbody: write error"); 5029*7c478bd9Sstevel@tonic-gate ExitStat = EX_IOERR; 5030*7c478bd9Sstevel@tonic-gate } 5031*7c478bd9Sstevel@tonic-gate 5032*7c478bd9Sstevel@tonic-gate errno = 0; 5033*7c478bd9Sstevel@tonic-gate } 5034*7c478bd9Sstevel@tonic-gate /* 5035*7c478bd9Sstevel@tonic-gate ** MAILFILE -- Send a message to a file. 5036*7c478bd9Sstevel@tonic-gate ** 5037*7c478bd9Sstevel@tonic-gate ** If the file has the set-user-ID/set-group-ID bits set, but NO 5038*7c478bd9Sstevel@tonic-gate ** execute bits, sendmail will try to become the owner of that file 5039*7c478bd9Sstevel@tonic-gate ** rather than the real user. Obviously, this only works if 5040*7c478bd9Sstevel@tonic-gate ** sendmail runs as root. 5041*7c478bd9Sstevel@tonic-gate ** 5042*7c478bd9Sstevel@tonic-gate ** This could be done as a subordinate mailer, except that it 5043*7c478bd9Sstevel@tonic-gate ** is used implicitly to save messages in ~/dead.letter. We 5044*7c478bd9Sstevel@tonic-gate ** view this as being sufficiently important as to include it 5045*7c478bd9Sstevel@tonic-gate ** here. For example, if the system is dying, we shouldn't have 5046*7c478bd9Sstevel@tonic-gate ** to create another process plus some pipes to save the message. 5047*7c478bd9Sstevel@tonic-gate ** 5048*7c478bd9Sstevel@tonic-gate ** Parameters: 5049*7c478bd9Sstevel@tonic-gate ** filename -- the name of the file to send to. 5050*7c478bd9Sstevel@tonic-gate ** mailer -- mailer definition for recipient -- if NULL, 5051*7c478bd9Sstevel@tonic-gate ** use FileMailer. 5052*7c478bd9Sstevel@tonic-gate ** ctladdr -- the controlling address header -- includes 5053*7c478bd9Sstevel@tonic-gate ** the userid/groupid to be when sending. 5054*7c478bd9Sstevel@tonic-gate ** sfflags -- flags for opening. 5055*7c478bd9Sstevel@tonic-gate ** e -- the current envelope. 5056*7c478bd9Sstevel@tonic-gate ** 5057*7c478bd9Sstevel@tonic-gate ** Returns: 5058*7c478bd9Sstevel@tonic-gate ** The exit code associated with the operation. 5059*7c478bd9Sstevel@tonic-gate ** 5060*7c478bd9Sstevel@tonic-gate ** Side Effects: 5061*7c478bd9Sstevel@tonic-gate ** none. 5062*7c478bd9Sstevel@tonic-gate */ 5063*7c478bd9Sstevel@tonic-gate 5064*7c478bd9Sstevel@tonic-gate # define RETURN(st) exit(st); 5065*7c478bd9Sstevel@tonic-gate 5066*7c478bd9Sstevel@tonic-gate static jmp_buf CtxMailfileTimeout; 5067*7c478bd9Sstevel@tonic-gate 5068*7c478bd9Sstevel@tonic-gate int 5069*7c478bd9Sstevel@tonic-gate mailfile(filename, mailer, ctladdr, sfflags, e) 5070*7c478bd9Sstevel@tonic-gate char *volatile filename; 5071*7c478bd9Sstevel@tonic-gate MAILER *volatile mailer; 5072*7c478bd9Sstevel@tonic-gate ADDRESS *ctladdr; 5073*7c478bd9Sstevel@tonic-gate volatile long sfflags; 5074*7c478bd9Sstevel@tonic-gate register ENVELOPE *e; 5075*7c478bd9Sstevel@tonic-gate { 5076*7c478bd9Sstevel@tonic-gate register SM_FILE_T *f; 5077*7c478bd9Sstevel@tonic-gate register pid_t pid = -1; 5078*7c478bd9Sstevel@tonic-gate volatile int mode; 5079*7c478bd9Sstevel@tonic-gate int len; 5080*7c478bd9Sstevel@tonic-gate off_t curoff; 5081*7c478bd9Sstevel@tonic-gate bool suidwarn = geteuid() == 0; 5082*7c478bd9Sstevel@tonic-gate char *p; 5083*7c478bd9Sstevel@tonic-gate char *volatile realfile; 5084*7c478bd9Sstevel@tonic-gate SM_EVENT *ev; 5085*7c478bd9Sstevel@tonic-gate char buf[MAXPATHLEN]; 5086*7c478bd9Sstevel@tonic-gate char targetfile[MAXPATHLEN]; 5087*7c478bd9Sstevel@tonic-gate 5088*7c478bd9Sstevel@tonic-gate if (tTd(11, 1)) 5089*7c478bd9Sstevel@tonic-gate { 5090*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile %s\n ctladdr=", filename); 5091*7c478bd9Sstevel@tonic-gate printaddr(sm_debug_file(), ctladdr, false); 5092*7c478bd9Sstevel@tonic-gate } 5093*7c478bd9Sstevel@tonic-gate 5094*7c478bd9Sstevel@tonic-gate if (mailer == NULL) 5095*7c478bd9Sstevel@tonic-gate mailer = FileMailer; 5096*7c478bd9Sstevel@tonic-gate 5097*7c478bd9Sstevel@tonic-gate if (e->e_xfp != NULL) 5098*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT); 5099*7c478bd9Sstevel@tonic-gate 5100*7c478bd9Sstevel@tonic-gate /* 5101*7c478bd9Sstevel@tonic-gate ** Special case /dev/null. This allows us to restrict file 5102*7c478bd9Sstevel@tonic-gate ** delivery to regular files only. 5103*7c478bd9Sstevel@tonic-gate */ 5104*7c478bd9Sstevel@tonic-gate 5105*7c478bd9Sstevel@tonic-gate if (sm_path_isdevnull(filename)) 5106*7c478bd9Sstevel@tonic-gate return EX_OK; 5107*7c478bd9Sstevel@tonic-gate 5108*7c478bd9Sstevel@tonic-gate /* check for 8-bit available */ 5109*7c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 5110*7c478bd9Sstevel@tonic-gate bitnset(M_7BITS, mailer->m_flags) && 5111*7c478bd9Sstevel@tonic-gate (bitset(EF_DONT_MIME, e->e_flags) || 5112*7c478bd9Sstevel@tonic-gate !(bitset(MM_MIME8BIT, MimeMode) || 5113*7c478bd9Sstevel@tonic-gate (bitset(EF_IS_MIME, e->e_flags) && 5114*7c478bd9Sstevel@tonic-gate bitset(MM_CVTMIME, MimeMode))))) 5115*7c478bd9Sstevel@tonic-gate { 5116*7c478bd9Sstevel@tonic-gate e->e_status = "5.6.3"; 5117*7c478bd9Sstevel@tonic-gate usrerrenh(e->e_status, 5118*7c478bd9Sstevel@tonic-gate "554 Cannot send 8-bit data to 7-bit destination"); 5119*7c478bd9Sstevel@tonic-gate errno = 0; 5120*7c478bd9Sstevel@tonic-gate return EX_DATAERR; 5121*7c478bd9Sstevel@tonic-gate } 5122*7c478bd9Sstevel@tonic-gate 5123*7c478bd9Sstevel@tonic-gate /* Find the actual file */ 5124*7c478bd9Sstevel@tonic-gate if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 5125*7c478bd9Sstevel@tonic-gate { 5126*7c478bd9Sstevel@tonic-gate len = strlen(SafeFileEnv); 5127*7c478bd9Sstevel@tonic-gate 5128*7c478bd9Sstevel@tonic-gate if (strncmp(SafeFileEnv, filename, len) == 0) 5129*7c478bd9Sstevel@tonic-gate filename += len; 5130*7c478bd9Sstevel@tonic-gate 5131*7c478bd9Sstevel@tonic-gate if (len + strlen(filename) + 1 >= sizeof targetfile) 5132*7c478bd9Sstevel@tonic-gate { 5133*7c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s/%s)", 5134*7c478bd9Sstevel@tonic-gate SafeFileEnv, filename); 5135*7c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 5136*7c478bd9Sstevel@tonic-gate } 5137*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(targetfile, SafeFileEnv, sizeof targetfile); 5138*7c478bd9Sstevel@tonic-gate realfile = targetfile + len; 5139*7c478bd9Sstevel@tonic-gate if (*filename == '/') 5140*7c478bd9Sstevel@tonic-gate filename++; 5141*7c478bd9Sstevel@tonic-gate if (*filename != '\0') 5142*7c478bd9Sstevel@tonic-gate { 5143*7c478bd9Sstevel@tonic-gate /* paranoia: trailing / should be removed in readcf */ 5144*7c478bd9Sstevel@tonic-gate if (targetfile[len - 1] != '/') 5145*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, 5146*7c478bd9Sstevel@tonic-gate "/", sizeof targetfile); 5147*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename, 5148*7c478bd9Sstevel@tonic-gate sizeof targetfile); 5149*7c478bd9Sstevel@tonic-gate } 5150*7c478bd9Sstevel@tonic-gate } 5151*7c478bd9Sstevel@tonic-gate else if (mailer->m_rootdir != NULL) 5152*7c478bd9Sstevel@tonic-gate { 5153*7c478bd9Sstevel@tonic-gate expand(mailer->m_rootdir, targetfile, sizeof targetfile, e); 5154*7c478bd9Sstevel@tonic-gate len = strlen(targetfile); 5155*7c478bd9Sstevel@tonic-gate 5156*7c478bd9Sstevel@tonic-gate if (strncmp(targetfile, filename, len) == 0) 5157*7c478bd9Sstevel@tonic-gate filename += len; 5158*7c478bd9Sstevel@tonic-gate 5159*7c478bd9Sstevel@tonic-gate if (len + strlen(filename) + 1 >= sizeof targetfile) 5160*7c478bd9Sstevel@tonic-gate { 5161*7c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s/%s)", 5162*7c478bd9Sstevel@tonic-gate targetfile, filename); 5163*7c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 5164*7c478bd9Sstevel@tonic-gate } 5165*7c478bd9Sstevel@tonic-gate realfile = targetfile + len; 5166*7c478bd9Sstevel@tonic-gate if (targetfile[len - 1] != '/') 5167*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, "/", sizeof targetfile); 5168*7c478bd9Sstevel@tonic-gate if (*filename == '/') 5169*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename + 1, 5170*7c478bd9Sstevel@tonic-gate sizeof targetfile); 5171*7c478bd9Sstevel@tonic-gate else 5172*7c478bd9Sstevel@tonic-gate (void) sm_strlcat(targetfile, filename, 5173*7c478bd9Sstevel@tonic-gate sizeof targetfile); 5174*7c478bd9Sstevel@tonic-gate } 5175*7c478bd9Sstevel@tonic-gate else 5176*7c478bd9Sstevel@tonic-gate { 5177*7c478bd9Sstevel@tonic-gate if (sm_strlcpy(targetfile, filename, sizeof targetfile) >= 5178*7c478bd9Sstevel@tonic-gate sizeof targetfile) 5179*7c478bd9Sstevel@tonic-gate { 5180*7c478bd9Sstevel@tonic-gate syserr("mailfile: filename too long (%s)", filename); 5181*7c478bd9Sstevel@tonic-gate return EX_CANTCREAT; 5182*7c478bd9Sstevel@tonic-gate } 5183*7c478bd9Sstevel@tonic-gate realfile = targetfile; 5184*7c478bd9Sstevel@tonic-gate } 5185*7c478bd9Sstevel@tonic-gate 5186*7c478bd9Sstevel@tonic-gate /* 5187*7c478bd9Sstevel@tonic-gate ** Fork so we can change permissions here. 5188*7c478bd9Sstevel@tonic-gate ** Note that we MUST use fork, not vfork, because of 5189*7c478bd9Sstevel@tonic-gate ** the complications of calling subroutines, etc. 5190*7c478bd9Sstevel@tonic-gate */ 5191*7c478bd9Sstevel@tonic-gate 5192*7c478bd9Sstevel@tonic-gate 5193*7c478bd9Sstevel@tonic-gate /* 5194*7c478bd9Sstevel@tonic-gate ** Dispose of SIGCHLD signal catchers that may be laying 5195*7c478bd9Sstevel@tonic-gate ** around so that the waitfor() below will get it. 5196*7c478bd9Sstevel@tonic-gate */ 5197*7c478bd9Sstevel@tonic-gate 5198*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGCHLD, SIG_DFL); 5199*7c478bd9Sstevel@tonic-gate 5200*7c478bd9Sstevel@tonic-gate DOFORK(fork); 5201*7c478bd9Sstevel@tonic-gate 5202*7c478bd9Sstevel@tonic-gate if (pid < 0) 5203*7c478bd9Sstevel@tonic-gate return EX_OSERR; 5204*7c478bd9Sstevel@tonic-gate else if (pid == 0) 5205*7c478bd9Sstevel@tonic-gate { 5206*7c478bd9Sstevel@tonic-gate /* child -- actually write to file */ 5207*7c478bd9Sstevel@tonic-gate struct stat stb; 5208*7c478bd9Sstevel@tonic-gate MCI mcibuf; 5209*7c478bd9Sstevel@tonic-gate int err; 5210*7c478bd9Sstevel@tonic-gate volatile int oflags = O_WRONLY|O_APPEND; 5211*7c478bd9Sstevel@tonic-gate 5212*7c478bd9Sstevel@tonic-gate /* Reset global flags */ 5213*7c478bd9Sstevel@tonic-gate RestartRequest = NULL; 5214*7c478bd9Sstevel@tonic-gate RestartWorkGroup = false; 5215*7c478bd9Sstevel@tonic-gate ShutdownRequest = NULL; 5216*7c478bd9Sstevel@tonic-gate PendingSignal = 0; 5217*7c478bd9Sstevel@tonic-gate CurrentPid = getpid(); 5218*7c478bd9Sstevel@tonic-gate 5219*7c478bd9Sstevel@tonic-gate if (e->e_lockfp != NULL) 5220*7c478bd9Sstevel@tonic-gate (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, 5221*7c478bd9Sstevel@tonic-gate NULL)); 5222*7c478bd9Sstevel@tonic-gate 5223*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGINT, SIG_DFL); 5224*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGHUP, SIG_DFL); 5225*7c478bd9Sstevel@tonic-gate (void) sm_signal(SIGTERM, SIG_DFL); 5226*7c478bd9Sstevel@tonic-gate (void) umask(OldUmask); 5227*7c478bd9Sstevel@tonic-gate e->e_to = filename; 5228*7c478bd9Sstevel@tonic-gate ExitStat = EX_OK; 5229*7c478bd9Sstevel@tonic-gate 5230*7c478bd9Sstevel@tonic-gate if (setjmp(CtxMailfileTimeout) != 0) 5231*7c478bd9Sstevel@tonic-gate { 5232*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5233*7c478bd9Sstevel@tonic-gate } 5234*7c478bd9Sstevel@tonic-gate 5235*7c478bd9Sstevel@tonic-gate if (TimeOuts.to_fileopen > 0) 5236*7c478bd9Sstevel@tonic-gate ev = sm_setevent(TimeOuts.to_fileopen, mailfiletimeout, 5237*7c478bd9Sstevel@tonic-gate 0); 5238*7c478bd9Sstevel@tonic-gate else 5239*7c478bd9Sstevel@tonic-gate ev = NULL; 5240*7c478bd9Sstevel@tonic-gate 5241*7c478bd9Sstevel@tonic-gate /* check file mode to see if set-user-ID */ 5242*7c478bd9Sstevel@tonic-gate if (stat(targetfile, &stb) < 0) 5243*7c478bd9Sstevel@tonic-gate mode = FileMode; 5244*7c478bd9Sstevel@tonic-gate else 5245*7c478bd9Sstevel@tonic-gate mode = stb.st_mode; 5246*7c478bd9Sstevel@tonic-gate 5247*7c478bd9Sstevel@tonic-gate /* limit the errors to those actually caused in the child */ 5248*7c478bd9Sstevel@tonic-gate errno = 0; 5249*7c478bd9Sstevel@tonic-gate ExitStat = EX_OK; 5250*7c478bd9Sstevel@tonic-gate 5251*7c478bd9Sstevel@tonic-gate /* Allow alias expansions to use the S_IS{U,G}ID bits */ 5252*7c478bd9Sstevel@tonic-gate if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) || 5253*7c478bd9Sstevel@tonic-gate bitset(SFF_RUNASREALUID, sfflags)) 5254*7c478bd9Sstevel@tonic-gate { 5255*7c478bd9Sstevel@tonic-gate /* ignore set-user-ID and set-group-ID bits */ 5256*7c478bd9Sstevel@tonic-gate mode &= ~(S_ISGID|S_ISUID); 5257*7c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 5258*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: ignoring set-user-ID/set-group-ID bits\n"); 5259*7c478bd9Sstevel@tonic-gate } 5260*7c478bd9Sstevel@tonic-gate 5261*7c478bd9Sstevel@tonic-gate /* we have to open the data file BEFORE setuid() */ 5262*7c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) 5263*7c478bd9Sstevel@tonic-gate { 5264*7c478bd9Sstevel@tonic-gate char *df = queuename(e, DATAFL_LETTER); 5265*7c478bd9Sstevel@tonic-gate 5266*7c478bd9Sstevel@tonic-gate e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, df, 5267*7c478bd9Sstevel@tonic-gate SM_IO_RDONLY_B, NULL); 5268*7c478bd9Sstevel@tonic-gate if (e->e_dfp == NULL) 5269*7c478bd9Sstevel@tonic-gate { 5270*7c478bd9Sstevel@tonic-gate syserr("mailfile: Cannot open %s for %s from %s", 5271*7c478bd9Sstevel@tonic-gate df, e->e_to, e->e_from.q_paddr); 5272*7c478bd9Sstevel@tonic-gate } 5273*7c478bd9Sstevel@tonic-gate } 5274*7c478bd9Sstevel@tonic-gate 5275*7c478bd9Sstevel@tonic-gate /* select a new user to run as */ 5276*7c478bd9Sstevel@tonic-gate if (!bitset(SFF_RUNASREALUID, sfflags)) 5277*7c478bd9Sstevel@tonic-gate { 5278*7c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 5279*7c478bd9Sstevel@tonic-gate { 5280*7c478bd9Sstevel@tonic-gate RealUserName = NULL; 5281*7c478bd9Sstevel@tonic-gate if (mailer->m_uid == NO_UID) 5282*7c478bd9Sstevel@tonic-gate RealUid = RunAsUid; 5283*7c478bd9Sstevel@tonic-gate else 5284*7c478bd9Sstevel@tonic-gate RealUid = mailer->m_uid; 5285*7c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && RealUid != RunAsUid) 5286*7c478bd9Sstevel@tonic-gate { 5287*7c478bd9Sstevel@tonic-gate /* Only root can change the uid */ 5288*7c478bd9Sstevel@tonic-gate syserr("mailfile: insufficient privileges to change uid, RunAsUid=%d, RealUid=%d", 5289*7c478bd9Sstevel@tonic-gate (int) RunAsUid, (int) RealUid); 5290*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5291*7c478bd9Sstevel@tonic-gate } 5292*7c478bd9Sstevel@tonic-gate } 5293*7c478bd9Sstevel@tonic-gate else if (bitset(S_ISUID, mode)) 5294*7c478bd9Sstevel@tonic-gate { 5295*7c478bd9Sstevel@tonic-gate RealUserName = NULL; 5296*7c478bd9Sstevel@tonic-gate RealUid = stb.st_uid; 5297*7c478bd9Sstevel@tonic-gate } 5298*7c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 5299*7c478bd9Sstevel@tonic-gate { 5300*7c478bd9Sstevel@tonic-gate if (ctladdr->q_ruser != NULL) 5301*7c478bd9Sstevel@tonic-gate RealUserName = ctladdr->q_ruser; 5302*7c478bd9Sstevel@tonic-gate else 5303*7c478bd9Sstevel@tonic-gate RealUserName = ctladdr->q_user; 5304*7c478bd9Sstevel@tonic-gate RealUid = ctladdr->q_uid; 5305*7c478bd9Sstevel@tonic-gate } 5306*7c478bd9Sstevel@tonic-gate else if (mailer != NULL && mailer->m_uid != NO_UID) 5307*7c478bd9Sstevel@tonic-gate { 5308*7c478bd9Sstevel@tonic-gate RealUserName = DefUser; 5309*7c478bd9Sstevel@tonic-gate RealUid = mailer->m_uid; 5310*7c478bd9Sstevel@tonic-gate } 5311*7c478bd9Sstevel@tonic-gate else 5312*7c478bd9Sstevel@tonic-gate { 5313*7c478bd9Sstevel@tonic-gate RealUserName = DefUser; 5314*7c478bd9Sstevel@tonic-gate RealUid = DefUid; 5315*7c478bd9Sstevel@tonic-gate } 5316*7c478bd9Sstevel@tonic-gate 5317*7c478bd9Sstevel@tonic-gate /* select a new group to run as */ 5318*7c478bd9Sstevel@tonic-gate if (bitnset(M_SPECIFIC_UID, mailer->m_flags)) 5319*7c478bd9Sstevel@tonic-gate { 5320*7c478bd9Sstevel@tonic-gate if (mailer->m_gid == NO_GID) 5321*7c478bd9Sstevel@tonic-gate RealGid = RunAsGid; 5322*7c478bd9Sstevel@tonic-gate else 5323*7c478bd9Sstevel@tonic-gate RealGid = mailer->m_gid; 5324*7c478bd9Sstevel@tonic-gate if (RunAsUid != 0 && 5325*7c478bd9Sstevel@tonic-gate (RealGid != getgid() || 5326*7c478bd9Sstevel@tonic-gate RealGid != getegid())) 5327*7c478bd9Sstevel@tonic-gate { 5328*7c478bd9Sstevel@tonic-gate /* Only root can change the gid */ 5329*7c478bd9Sstevel@tonic-gate syserr("mailfile: insufficient privileges to change gid, RealGid=%d, RunAsUid=%d, gid=%d, egid=%d", 5330*7c478bd9Sstevel@tonic-gate (int) RealGid, (int) RunAsUid, 5331*7c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 5332*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5333*7c478bd9Sstevel@tonic-gate } 5334*7c478bd9Sstevel@tonic-gate } 5335*7c478bd9Sstevel@tonic-gate else if (bitset(S_ISGID, mode)) 5336*7c478bd9Sstevel@tonic-gate RealGid = stb.st_gid; 5337*7c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && 5338*7c478bd9Sstevel@tonic-gate ctladdr->q_uid == DefUid && 5339*7c478bd9Sstevel@tonic-gate ctladdr->q_gid == 0) 5340*7c478bd9Sstevel@tonic-gate { 5341*7c478bd9Sstevel@tonic-gate /* 5342*7c478bd9Sstevel@tonic-gate ** Special case: This means it is an 5343*7c478bd9Sstevel@tonic-gate ** alias and we should act as DefaultUser. 5344*7c478bd9Sstevel@tonic-gate ** See alias()'s comments. 5345*7c478bd9Sstevel@tonic-gate */ 5346*7c478bd9Sstevel@tonic-gate 5347*7c478bd9Sstevel@tonic-gate RealGid = DefGid; 5348*7c478bd9Sstevel@tonic-gate RealUserName = DefUser; 5349*7c478bd9Sstevel@tonic-gate } 5350*7c478bd9Sstevel@tonic-gate else if (ctladdr != NULL && ctladdr->q_uid != 0) 5351*7c478bd9Sstevel@tonic-gate RealGid = ctladdr->q_gid; 5352*7c478bd9Sstevel@tonic-gate else if (mailer != NULL && mailer->m_gid != NO_GID) 5353*7c478bd9Sstevel@tonic-gate RealGid = mailer->m_gid; 5354*7c478bd9Sstevel@tonic-gate else 5355*7c478bd9Sstevel@tonic-gate RealGid = DefGid; 5356*7c478bd9Sstevel@tonic-gate } 5357*7c478bd9Sstevel@tonic-gate 5358*7c478bd9Sstevel@tonic-gate /* last ditch */ 5359*7c478bd9Sstevel@tonic-gate if (!bitset(SFF_ROOTOK, sfflags)) 5360*7c478bd9Sstevel@tonic-gate { 5361*7c478bd9Sstevel@tonic-gate if (RealUid == 0) 5362*7c478bd9Sstevel@tonic-gate RealUid = DefUid; 5363*7c478bd9Sstevel@tonic-gate if (RealGid == 0) 5364*7c478bd9Sstevel@tonic-gate RealGid = DefGid; 5365*7c478bd9Sstevel@tonic-gate } 5366*7c478bd9Sstevel@tonic-gate 5367*7c478bd9Sstevel@tonic-gate /* set group id list (needs /etc/group access) */ 5368*7c478bd9Sstevel@tonic-gate if (RealUserName != NULL && !DontInitGroups) 5369*7c478bd9Sstevel@tonic-gate { 5370*7c478bd9Sstevel@tonic-gate if (initgroups(RealUserName, RealGid) == -1 && suidwarn) 5371*7c478bd9Sstevel@tonic-gate { 5372*7c478bd9Sstevel@tonic-gate syserr("mailfile: initgroups(%s, %d) failed", 5373*7c478bd9Sstevel@tonic-gate RealUserName, RealGid); 5374*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5375*7c478bd9Sstevel@tonic-gate } 5376*7c478bd9Sstevel@tonic-gate } 5377*7c478bd9Sstevel@tonic-gate else 5378*7c478bd9Sstevel@tonic-gate { 5379*7c478bd9Sstevel@tonic-gate GIDSET_T gidset[1]; 5380*7c478bd9Sstevel@tonic-gate 5381*7c478bd9Sstevel@tonic-gate gidset[0] = RealGid; 5382*7c478bd9Sstevel@tonic-gate if (setgroups(1, gidset) == -1 && suidwarn) 5383*7c478bd9Sstevel@tonic-gate { 5384*7c478bd9Sstevel@tonic-gate syserr("mailfile: setgroups() failed"); 5385*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5386*7c478bd9Sstevel@tonic-gate } 5387*7c478bd9Sstevel@tonic-gate } 5388*7c478bd9Sstevel@tonic-gate 5389*7c478bd9Sstevel@tonic-gate /* 5390*7c478bd9Sstevel@tonic-gate ** If you have a safe environment, go into it. 5391*7c478bd9Sstevel@tonic-gate */ 5392*7c478bd9Sstevel@tonic-gate 5393*7c478bd9Sstevel@tonic-gate if (realfile != targetfile) 5394*7c478bd9Sstevel@tonic-gate { 5395*7c478bd9Sstevel@tonic-gate char save; 5396*7c478bd9Sstevel@tonic-gate 5397*7c478bd9Sstevel@tonic-gate save = *realfile; 5398*7c478bd9Sstevel@tonic-gate *realfile = '\0'; 5399*7c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 5400*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: chroot %s\n", targetfile); 5401*7c478bd9Sstevel@tonic-gate if (chroot(targetfile) < 0) 5402*7c478bd9Sstevel@tonic-gate { 5403*7c478bd9Sstevel@tonic-gate syserr("mailfile: Cannot chroot(%s)", 5404*7c478bd9Sstevel@tonic-gate targetfile); 5405*7c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 5406*7c478bd9Sstevel@tonic-gate } 5407*7c478bd9Sstevel@tonic-gate *realfile = save; 5408*7c478bd9Sstevel@tonic-gate } 5409*7c478bd9Sstevel@tonic-gate 5410*7c478bd9Sstevel@tonic-gate if (tTd(11, 40)) 5411*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: deliver to %s\n", realfile); 5412*7c478bd9Sstevel@tonic-gate 5413*7c478bd9Sstevel@tonic-gate if (chdir("/") < 0) 5414*7c478bd9Sstevel@tonic-gate { 5415*7c478bd9Sstevel@tonic-gate syserr("mailfile: cannot chdir(/)"); 5416*7c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 5417*7c478bd9Sstevel@tonic-gate } 5418*7c478bd9Sstevel@tonic-gate 5419*7c478bd9Sstevel@tonic-gate /* now reset the group and user ids */ 5420*7c478bd9Sstevel@tonic-gate endpwent(); 5421*7c478bd9Sstevel@tonic-gate sm_mbdb_terminate(); 5422*7c478bd9Sstevel@tonic-gate if (setgid(RealGid) < 0 && suidwarn) 5423*7c478bd9Sstevel@tonic-gate { 5424*7c478bd9Sstevel@tonic-gate syserr("mailfile: setgid(%ld) failed", (long) RealGid); 5425*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5426*7c478bd9Sstevel@tonic-gate } 5427*7c478bd9Sstevel@tonic-gate vendor_set_uid(RealUid); 5428*7c478bd9Sstevel@tonic-gate if (setuid(RealUid) < 0 && suidwarn) 5429*7c478bd9Sstevel@tonic-gate { 5430*7c478bd9Sstevel@tonic-gate syserr("mailfile: setuid(%ld) failed", (long) RealUid); 5431*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5432*7c478bd9Sstevel@tonic-gate } 5433*7c478bd9Sstevel@tonic-gate 5434*7c478bd9Sstevel@tonic-gate if (tTd(11, 2)) 5435*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n", 5436*7c478bd9Sstevel@tonic-gate (int) getuid(), (int) geteuid(), 5437*7c478bd9Sstevel@tonic-gate (int) getgid(), (int) getegid()); 5438*7c478bd9Sstevel@tonic-gate 5439*7c478bd9Sstevel@tonic-gate 5440*7c478bd9Sstevel@tonic-gate /* move into some "safe" directory */ 5441*7c478bd9Sstevel@tonic-gate if (mailer->m_execdir != NULL) 5442*7c478bd9Sstevel@tonic-gate { 5443*7c478bd9Sstevel@tonic-gate char *q; 5444*7c478bd9Sstevel@tonic-gate 5445*7c478bd9Sstevel@tonic-gate for (p = mailer->m_execdir; p != NULL; p = q) 5446*7c478bd9Sstevel@tonic-gate { 5447*7c478bd9Sstevel@tonic-gate q = strchr(p, ':'); 5448*7c478bd9Sstevel@tonic-gate if (q != NULL) 5449*7c478bd9Sstevel@tonic-gate *q = '\0'; 5450*7c478bd9Sstevel@tonic-gate expand(p, buf, sizeof buf, e); 5451*7c478bd9Sstevel@tonic-gate if (q != NULL) 5452*7c478bd9Sstevel@tonic-gate *q++ = ':'; 5453*7c478bd9Sstevel@tonic-gate if (tTd(11, 20)) 5454*7c478bd9Sstevel@tonic-gate sm_dprintf("mailfile: trydir %s\n", 5455*7c478bd9Sstevel@tonic-gate buf); 5456*7c478bd9Sstevel@tonic-gate if (buf[0] != '\0' && chdir(buf) >= 0) 5457*7c478bd9Sstevel@tonic-gate break; 5458*7c478bd9Sstevel@tonic-gate } 5459*7c478bd9Sstevel@tonic-gate } 5460*7c478bd9Sstevel@tonic-gate 5461*7c478bd9Sstevel@tonic-gate /* 5462*7c478bd9Sstevel@tonic-gate ** Recheck the file after we have assumed the ID of the 5463*7c478bd9Sstevel@tonic-gate ** delivery user to make sure we can deliver to it as 5464*7c478bd9Sstevel@tonic-gate ** that user. This is necessary if sendmail is running 5465*7c478bd9Sstevel@tonic-gate ** as root and the file is on an NFS mount which treats 5466*7c478bd9Sstevel@tonic-gate ** root as nobody. 5467*7c478bd9Sstevel@tonic-gate */ 5468*7c478bd9Sstevel@tonic-gate 5469*7c478bd9Sstevel@tonic-gate #if HASLSTAT 5470*7c478bd9Sstevel@tonic-gate if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 5471*7c478bd9Sstevel@tonic-gate err = stat(realfile, &stb); 5472*7c478bd9Sstevel@tonic-gate else 5473*7c478bd9Sstevel@tonic-gate err = lstat(realfile, &stb); 5474*7c478bd9Sstevel@tonic-gate #else /* HASLSTAT */ 5475*7c478bd9Sstevel@tonic-gate err = stat(realfile, &stb); 5476*7c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */ 5477*7c478bd9Sstevel@tonic-gate 5478*7c478bd9Sstevel@tonic-gate if (err < 0) 5479*7c478bd9Sstevel@tonic-gate { 5480*7c478bd9Sstevel@tonic-gate stb.st_mode = ST_MODE_NOFILE; 5481*7c478bd9Sstevel@tonic-gate mode = FileMode; 5482*7c478bd9Sstevel@tonic-gate oflags |= O_CREAT|O_EXCL; 5483*7c478bd9Sstevel@tonic-gate } 5484*7c478bd9Sstevel@tonic-gate else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) || 5485*7c478bd9Sstevel@tonic-gate (!bitnset(DBS_FILEDELIVERYTOHARDLINK, 5486*7c478bd9Sstevel@tonic-gate DontBlameSendmail) && 5487*7c478bd9Sstevel@tonic-gate stb.st_nlink != 1) || 5488*7c478bd9Sstevel@tonic-gate (realfile != targetfile && !S_ISREG(mode))) 5489*7c478bd9Sstevel@tonic-gate exit(EX_CANTCREAT); 5490*7c478bd9Sstevel@tonic-gate else 5491*7c478bd9Sstevel@tonic-gate mode = stb.st_mode; 5492*7c478bd9Sstevel@tonic-gate 5493*7c478bd9Sstevel@tonic-gate if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail)) 5494*7c478bd9Sstevel@tonic-gate sfflags |= SFF_NOSLINK; 5495*7c478bd9Sstevel@tonic-gate if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail)) 5496*7c478bd9Sstevel@tonic-gate sfflags |= SFF_NOHLINK; 5497*7c478bd9Sstevel@tonic-gate sfflags &= ~SFF_OPENASROOT; 5498*7c478bd9Sstevel@tonic-gate f = safefopen(realfile, oflags, mode, sfflags); 5499*7c478bd9Sstevel@tonic-gate if (f == NULL) 5500*7c478bd9Sstevel@tonic-gate { 5501*7c478bd9Sstevel@tonic-gate if (transienterror(errno)) 5502*7c478bd9Sstevel@tonic-gate { 5503*7c478bd9Sstevel@tonic-gate usrerr("454 4.3.0 cannot open %s: %s", 5504*7c478bd9Sstevel@tonic-gate shortenstring(realfile, MAXSHORTSTR), 5505*7c478bd9Sstevel@tonic-gate sm_errstring(errno)); 5506*7c478bd9Sstevel@tonic-gate RETURN(EX_TEMPFAIL); 5507*7c478bd9Sstevel@tonic-gate } 5508*7c478bd9Sstevel@tonic-gate else 5509*7c478bd9Sstevel@tonic-gate { 5510*7c478bd9Sstevel@tonic-gate usrerr("554 5.3.0 cannot open %s: %s", 5511*7c478bd9Sstevel@tonic-gate shortenstring(realfile, MAXSHORTSTR), 5512*7c478bd9Sstevel@tonic-gate sm_errstring(errno)); 5513*7c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 5514*7c478bd9Sstevel@tonic-gate } 5515*7c478bd9Sstevel@tonic-gate } 5516*7c478bd9Sstevel@tonic-gate if (filechanged(realfile, sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 5517*7c478bd9Sstevel@tonic-gate &stb)) 5518*7c478bd9Sstevel@tonic-gate { 5519*7c478bd9Sstevel@tonic-gate syserr("554 5.3.0 file changed after open"); 5520*7c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 5521*7c478bd9Sstevel@tonic-gate } 5522*7c478bd9Sstevel@tonic-gate if (fstat(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), &stb) < 0) 5523*7c478bd9Sstevel@tonic-gate { 5524*7c478bd9Sstevel@tonic-gate syserr("554 5.3.0 cannot fstat %s", 5525*7c478bd9Sstevel@tonic-gate sm_errstring(errno)); 5526*7c478bd9Sstevel@tonic-gate RETURN(EX_CANTCREAT); 5527*7c478bd9Sstevel@tonic-gate } 5528*7c478bd9Sstevel@tonic-gate 5529*7c478bd9Sstevel@tonic-gate curoff = stb.st_size; 5530*7c478bd9Sstevel@tonic-gate 5531*7c478bd9Sstevel@tonic-gate if (ev != NULL) 5532*7c478bd9Sstevel@tonic-gate sm_clrevent(ev); 5533*7c478bd9Sstevel@tonic-gate 5534*7c478bd9Sstevel@tonic-gate memset(&mcibuf, '\0', sizeof mcibuf); 5535*7c478bd9Sstevel@tonic-gate mcibuf.mci_mailer = mailer; 5536*7c478bd9Sstevel@tonic-gate mcibuf.mci_out = f; 5537*7c478bd9Sstevel@tonic-gate if (bitnset(M_7BITS, mailer->m_flags)) 5538*7c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_7BIT; 5539*7c478bd9Sstevel@tonic-gate 5540*7c478bd9Sstevel@tonic-gate /* clear out per-message flags from connection structure */ 5541*7c478bd9Sstevel@tonic-gate mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); 5542*7c478bd9Sstevel@tonic-gate 5543*7c478bd9Sstevel@tonic-gate if (bitset(EF_HAS8BIT, e->e_flags) && 5544*7c478bd9Sstevel@tonic-gate !bitset(EF_DONT_MIME, e->e_flags) && 5545*7c478bd9Sstevel@tonic-gate bitnset(M_7BITS, mailer->m_flags)) 5546*7c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_CVT8TO7; 5547*7c478bd9Sstevel@tonic-gate 5548*7c478bd9Sstevel@tonic-gate #if MIME7TO8 5549*7c478bd9Sstevel@tonic-gate if (bitnset(M_MAKE8BIT, mailer->m_flags) && 5550*7c478bd9Sstevel@tonic-gate !bitset(MCIF_7BIT, mcibuf.mci_flags) && 5551*7c478bd9Sstevel@tonic-gate (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && 5552*7c478bd9Sstevel@tonic-gate (sm_strcasecmp(p, "quoted-printable") == 0 || 5553*7c478bd9Sstevel@tonic-gate sm_strcasecmp(p, "base64") == 0) && 5554*7c478bd9Sstevel@tonic-gate (p = hvalue("Content-Type", e->e_header)) != NULL) 5555*7c478bd9Sstevel@tonic-gate { 5556*7c478bd9Sstevel@tonic-gate /* may want to convert 7 -> 8 */ 5557*7c478bd9Sstevel@tonic-gate /* XXX should really parse it here -- and use a class XXX */ 5558*7c478bd9Sstevel@tonic-gate if (sm_strncasecmp(p, "text/plain", 10) == 0 && 5559*7c478bd9Sstevel@tonic-gate (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) 5560*7c478bd9Sstevel@tonic-gate mcibuf.mci_flags |= MCIF_CVT7TO8; 5561*7c478bd9Sstevel@tonic-gate } 5562*7c478bd9Sstevel@tonic-gate #endif /* MIME7TO8 */ 5563*7c478bd9Sstevel@tonic-gate 5564*7c478bd9Sstevel@tonic-gate putfromline(&mcibuf, e); 5565*7c478bd9Sstevel@tonic-gate (*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER); 5566*7c478bd9Sstevel@tonic-gate (*e->e_putbody)(&mcibuf, e, NULL); 5567*7c478bd9Sstevel@tonic-gate putline("\n", &mcibuf); 5568*7c478bd9Sstevel@tonic-gate if (sm_io_flush(f, SM_TIME_DEFAULT) != 0 || 5569*7c478bd9Sstevel@tonic-gate (SuperSafe != SAFE_NO && 5570*7c478bd9Sstevel@tonic-gate fsync(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL)) < 0) || 5571*7c478bd9Sstevel@tonic-gate sm_io_error(f)) 5572*7c478bd9Sstevel@tonic-gate { 5573*7c478bd9Sstevel@tonic-gate setstat(EX_IOERR); 5574*7c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE 5575*7c478bd9Sstevel@tonic-gate (void) ftruncate(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 5576*7c478bd9Sstevel@tonic-gate curoff); 5577*7c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */ 5578*7c478bd9Sstevel@tonic-gate } 5579*7c478bd9Sstevel@tonic-gate 5580*7c478bd9Sstevel@tonic-gate /* reset ISUID & ISGID bits for paranoid systems */ 5581*7c478bd9Sstevel@tonic-gate #if HASFCHMOD 5582*7c478bd9Sstevel@tonic-gate (void) fchmod(sm_io_getinfo(f, SM_IO_WHAT_FD, NULL), 5583*7c478bd9Sstevel@tonic-gate (MODE_T) mode); 5584*7c478bd9Sstevel@tonic-gate #else /* HASFCHMOD */ 5585*7c478bd9Sstevel@tonic-gate (void) chmod(filename, (MODE_T) mode); 5586*7c478bd9Sstevel@tonic-gate #endif /* HASFCHMOD */ 5587*7c478bd9Sstevel@tonic-gate if (sm_io_close(f, SM_TIME_DEFAULT) < 0) 5588*7c478bd9Sstevel@tonic-gate setstat(EX_IOERR); 5589*7c478bd9Sstevel@tonic-gate (void) sm_io_flush(smioout, SM_TIME_DEFAULT); 5590*7c478bd9Sstevel@tonic-gate (void) setuid(RealUid); 5591*7c478bd9Sstevel@tonic-gate exit(ExitStat); 5592*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 5593*7c478bd9Sstevel@tonic-gate } 5594*7c478bd9Sstevel@tonic-gate else 5595*7c478bd9Sstevel@tonic-gate { 5596*7c478bd9Sstevel@tonic-gate /* parent -- wait for exit status */ 5597*7c478bd9Sstevel@tonic-gate int st; 5598*7c478bd9Sstevel@tonic-gate 5599*7c478bd9Sstevel@tonic-gate st = waitfor(pid); 5600*7c478bd9Sstevel@tonic-gate if (st == -1) 5601*7c478bd9Sstevel@tonic-gate { 5602*7c478bd9Sstevel@tonic-gate syserr("mailfile: %s: wait", mailer->m_name); 5603*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 5604*7c478bd9Sstevel@tonic-gate } 5605*7c478bd9Sstevel@tonic-gate if (WIFEXITED(st)) 5606*7c478bd9Sstevel@tonic-gate { 5607*7c478bd9Sstevel@tonic-gate errno = 0; 5608*7c478bd9Sstevel@tonic-gate return (WEXITSTATUS(st)); 5609*7c478bd9Sstevel@tonic-gate } 5610*7c478bd9Sstevel@tonic-gate else 5611*7c478bd9Sstevel@tonic-gate { 5612*7c478bd9Sstevel@tonic-gate syserr("mailfile: %s: child died on signal %d", 5613*7c478bd9Sstevel@tonic-gate mailer->m_name, st); 5614*7c478bd9Sstevel@tonic-gate return EX_UNAVAILABLE; 5615*7c478bd9Sstevel@tonic-gate } 5616*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 5617*7c478bd9Sstevel@tonic-gate } 5618*7c478bd9Sstevel@tonic-gate return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ 5619*7c478bd9Sstevel@tonic-gate } 5620*7c478bd9Sstevel@tonic-gate 5621*7c478bd9Sstevel@tonic-gate static void 5622*7c478bd9Sstevel@tonic-gate mailfiletimeout(ignore) 5623*7c478bd9Sstevel@tonic-gate int ignore; 5624*7c478bd9Sstevel@tonic-gate { 5625*7c478bd9Sstevel@tonic-gate /* 5626*7c478bd9Sstevel@tonic-gate ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 5627*7c478bd9Sstevel@tonic-gate ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 5628*7c478bd9Sstevel@tonic-gate ** DOING. 5629*7c478bd9Sstevel@tonic-gate */ 5630*7c478bd9Sstevel@tonic-gate 5631*7c478bd9Sstevel@tonic-gate errno = ETIMEDOUT; 5632*7c478bd9Sstevel@tonic-gate longjmp(CtxMailfileTimeout, 1); 5633*7c478bd9Sstevel@tonic-gate } 5634*7c478bd9Sstevel@tonic-gate /* 5635*7c478bd9Sstevel@tonic-gate ** HOSTSIGNATURE -- return the "signature" for a host. 5636*7c478bd9Sstevel@tonic-gate ** 5637*7c478bd9Sstevel@tonic-gate ** The signature describes how we are going to send this -- it 5638*7c478bd9Sstevel@tonic-gate ** can be just the hostname (for non-Internet hosts) or can be 5639*7c478bd9Sstevel@tonic-gate ** an ordered list of MX hosts. 5640*7c478bd9Sstevel@tonic-gate ** 5641*7c478bd9Sstevel@tonic-gate ** Parameters: 5642*7c478bd9Sstevel@tonic-gate ** m -- the mailer describing this host. 5643*7c478bd9Sstevel@tonic-gate ** host -- the host name. 5644*7c478bd9Sstevel@tonic-gate ** 5645*7c478bd9Sstevel@tonic-gate ** Returns: 5646*7c478bd9Sstevel@tonic-gate ** The signature for this host. 5647*7c478bd9Sstevel@tonic-gate ** 5648*7c478bd9Sstevel@tonic-gate ** Side Effects: 5649*7c478bd9Sstevel@tonic-gate ** Can tweak the symbol table. 5650*7c478bd9Sstevel@tonic-gate */ 5651*7c478bd9Sstevel@tonic-gate 5652*7c478bd9Sstevel@tonic-gate #define MAXHOSTSIGNATURE 8192 /* max len of hostsignature */ 5653*7c478bd9Sstevel@tonic-gate 5654*7c478bd9Sstevel@tonic-gate char * 5655*7c478bd9Sstevel@tonic-gate hostsignature(m, host) 5656*7c478bd9Sstevel@tonic-gate register MAILER *m; 5657*7c478bd9Sstevel@tonic-gate char *host; 5658*7c478bd9Sstevel@tonic-gate { 5659*7c478bd9Sstevel@tonic-gate register char *p; 5660*7c478bd9Sstevel@tonic-gate register STAB *s; 5661*7c478bd9Sstevel@tonic-gate time_t now; 5662*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 5663*7c478bd9Sstevel@tonic-gate char sep = ':'; 5664*7c478bd9Sstevel@tonic-gate char prevsep = ':'; 5665*7c478bd9Sstevel@tonic-gate int i; 5666*7c478bd9Sstevel@tonic-gate int len; 5667*7c478bd9Sstevel@tonic-gate int nmx; 5668*7c478bd9Sstevel@tonic-gate int hl; 5669*7c478bd9Sstevel@tonic-gate char *hp; 5670*7c478bd9Sstevel@tonic-gate char *endp; 5671*7c478bd9Sstevel@tonic-gate int oldoptions = _res.options; 5672*7c478bd9Sstevel@tonic-gate char *mxhosts[MAXMXHOSTS + 1]; 5673*7c478bd9Sstevel@tonic-gate unsigned short mxprefs[MAXMXHOSTS + 1]; 5674*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 5675*7c478bd9Sstevel@tonic-gate 5676*7c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 5677*7c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(%s)\n", host); 5678*7c478bd9Sstevel@tonic-gate 5679*7c478bd9Sstevel@tonic-gate /* 5680*7c478bd9Sstevel@tonic-gate ** If local delivery (and not remote), just return a constant. 5681*7c478bd9Sstevel@tonic-gate */ 5682*7c478bd9Sstevel@tonic-gate 5683*7c478bd9Sstevel@tonic-gate if (bitnset(M_LOCALMAILER, m->m_flags) && 5684*7c478bd9Sstevel@tonic-gate strcmp(m->m_mailer, "[IPC]") != 0 && 5685*7c478bd9Sstevel@tonic-gate !(m->m_argv[0] != NULL && strcmp(m->m_argv[0], "TCP") == 0)) 5686*7c478bd9Sstevel@tonic-gate return "localhost"; 5687*7c478bd9Sstevel@tonic-gate 5688*7c478bd9Sstevel@tonic-gate /* an empty host does not have MX records */ 5689*7c478bd9Sstevel@tonic-gate if (*host == '\0') 5690*7c478bd9Sstevel@tonic-gate return "_empty_"; 5691*7c478bd9Sstevel@tonic-gate 5692*7c478bd9Sstevel@tonic-gate /* 5693*7c478bd9Sstevel@tonic-gate ** Check to see if this uses IPC -- if not, it can't have MX records. 5694*7c478bd9Sstevel@tonic-gate */ 5695*7c478bd9Sstevel@tonic-gate 5696*7c478bd9Sstevel@tonic-gate if (strcmp(m->m_mailer, "[IPC]") != 0 || 5697*7c478bd9Sstevel@tonic-gate CurEnv->e_sendmode == SM_DEFER) 5698*7c478bd9Sstevel@tonic-gate { 5699*7c478bd9Sstevel@tonic-gate /* just an ordinary mailer or deferred mode */ 5700*7c478bd9Sstevel@tonic-gate return host; 5701*7c478bd9Sstevel@tonic-gate } 5702*7c478bd9Sstevel@tonic-gate #if NETUNIX 5703*7c478bd9Sstevel@tonic-gate else if (m->m_argv[0] != NULL && 5704*7c478bd9Sstevel@tonic-gate strcmp(m->m_argv[0], "FILE") == 0) 5705*7c478bd9Sstevel@tonic-gate { 5706*7c478bd9Sstevel@tonic-gate /* rendezvous in the file system, no MX records */ 5707*7c478bd9Sstevel@tonic-gate return host; 5708*7c478bd9Sstevel@tonic-gate } 5709*7c478bd9Sstevel@tonic-gate #endif /* NETUNIX */ 5710*7c478bd9Sstevel@tonic-gate 5711*7c478bd9Sstevel@tonic-gate /* 5712*7c478bd9Sstevel@tonic-gate ** Look it up in the symbol table. 5713*7c478bd9Sstevel@tonic-gate */ 5714*7c478bd9Sstevel@tonic-gate 5715*7c478bd9Sstevel@tonic-gate now = curtime(); 5716*7c478bd9Sstevel@tonic-gate s = stab(host, ST_HOSTSIG, ST_ENTER); 5717*7c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 5718*7c478bd9Sstevel@tonic-gate { 5719*7c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_exp >= now) 5720*7c478bd9Sstevel@tonic-gate { 5721*7c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 5722*7c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(): stab(%s) found %s\n", host, 5723*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig); 5724*7c478bd9Sstevel@tonic-gate return s->s_hostsig.hs_sig; 5725*7c478bd9Sstevel@tonic-gate } 5726*7c478bd9Sstevel@tonic-gate 5727*7c478bd9Sstevel@tonic-gate /* signature is expired: clear it */ 5728*7c478bd9Sstevel@tonic-gate sm_free(s->s_hostsig.hs_sig); 5729*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = NULL; 5730*7c478bd9Sstevel@tonic-gate } 5731*7c478bd9Sstevel@tonic-gate 5732*7c478bd9Sstevel@tonic-gate /* set default TTL */ 5733*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_exp = now + SM_DEFAULT_TTL; 5734*7c478bd9Sstevel@tonic-gate 5735*7c478bd9Sstevel@tonic-gate /* 5736*7c478bd9Sstevel@tonic-gate ** Not already there or expired -- create a signature. 5737*7c478bd9Sstevel@tonic-gate */ 5738*7c478bd9Sstevel@tonic-gate 5739*7c478bd9Sstevel@tonic-gate #if NAMED_BIND 5740*7c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 5741*7c478bd9Sstevel@tonic-gate _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ 5742*7c478bd9Sstevel@tonic-gate 5743*7c478bd9Sstevel@tonic-gate for (hp = host; hp != NULL; hp = endp) 5744*7c478bd9Sstevel@tonic-gate { 5745*7c478bd9Sstevel@tonic-gate #if NETINET6 5746*7c478bd9Sstevel@tonic-gate if (*hp == '[') 5747*7c478bd9Sstevel@tonic-gate { 5748*7c478bd9Sstevel@tonic-gate endp = strchr(hp + 1, ']'); 5749*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5750*7c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 5751*7c478bd9Sstevel@tonic-gate } 5752*7c478bd9Sstevel@tonic-gate else 5753*7c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 5754*7c478bd9Sstevel@tonic-gate #else /* NETINET6 */ 5755*7c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 5756*7c478bd9Sstevel@tonic-gate #endif /* NETINET6 */ 5757*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5758*7c478bd9Sstevel@tonic-gate { 5759*7c478bd9Sstevel@tonic-gate sep = *endp; 5760*7c478bd9Sstevel@tonic-gate *endp = '\0'; 5761*7c478bd9Sstevel@tonic-gate } 5762*7c478bd9Sstevel@tonic-gate 5763*7c478bd9Sstevel@tonic-gate if (bitnset(M_NOMX, m->m_flags)) 5764*7c478bd9Sstevel@tonic-gate { 5765*7c478bd9Sstevel@tonic-gate /* skip MX lookups */ 5766*7c478bd9Sstevel@tonic-gate nmx = 1; 5767*7c478bd9Sstevel@tonic-gate mxhosts[0] = hp; 5768*7c478bd9Sstevel@tonic-gate } 5769*7c478bd9Sstevel@tonic-gate else 5770*7c478bd9Sstevel@tonic-gate { 5771*7c478bd9Sstevel@tonic-gate auto int rcode; 5772*7c478bd9Sstevel@tonic-gate int ttl; 5773*7c478bd9Sstevel@tonic-gate 5774*7c478bd9Sstevel@tonic-gate nmx = getmxrr(hp, mxhosts, mxprefs, true, &rcode, true, 5775*7c478bd9Sstevel@tonic-gate &ttl); 5776*7c478bd9Sstevel@tonic-gate if (nmx <= 0) 5777*7c478bd9Sstevel@tonic-gate { 5778*7c478bd9Sstevel@tonic-gate int save_errno; 5779*7c478bd9Sstevel@tonic-gate register MCI *mci; 5780*7c478bd9Sstevel@tonic-gate 5781*7c478bd9Sstevel@tonic-gate /* update the connection info for this host */ 5782*7c478bd9Sstevel@tonic-gate save_errno = errno; 5783*7c478bd9Sstevel@tonic-gate mci = mci_get(hp, m); 5784*7c478bd9Sstevel@tonic-gate mci->mci_errno = save_errno; 5785*7c478bd9Sstevel@tonic-gate mci->mci_herrno = h_errno; 5786*7c478bd9Sstevel@tonic-gate mci->mci_lastuse = now; 5787*7c478bd9Sstevel@tonic-gate if (rcode == EX_NOHOST) 5788*7c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, "5.1.2", 5789*7c478bd9Sstevel@tonic-gate "550 Host unknown"); 5790*7c478bd9Sstevel@tonic-gate else 5791*7c478bd9Sstevel@tonic-gate mci_setstat(mci, rcode, NULL, NULL); 5792*7c478bd9Sstevel@tonic-gate 5793*7c478bd9Sstevel@tonic-gate /* use the original host name as signature */ 5794*7c478bd9Sstevel@tonic-gate nmx = 1; 5795*7c478bd9Sstevel@tonic-gate mxhosts[0] = hp; 5796*7c478bd9Sstevel@tonic-gate } 5797*7c478bd9Sstevel@tonic-gate if (tTd(17, 3)) 5798*7c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(): getmxrr() returned %d, mxhosts[0]=%s\n", 5799*7c478bd9Sstevel@tonic-gate nmx, mxhosts[0]); 5800*7c478bd9Sstevel@tonic-gate 5801*7c478bd9Sstevel@tonic-gate /* 5802*7c478bd9Sstevel@tonic-gate ** Set new TTL: we use only one! 5803*7c478bd9Sstevel@tonic-gate ** We could try to use the minimum instead. 5804*7c478bd9Sstevel@tonic-gate */ 5805*7c478bd9Sstevel@tonic-gate 5806*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_exp = now + SM_MIN(ttl, SM_DEFAULT_TTL); 5807*7c478bd9Sstevel@tonic-gate } 5808*7c478bd9Sstevel@tonic-gate 5809*7c478bd9Sstevel@tonic-gate len = 0; 5810*7c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 5811*7c478bd9Sstevel@tonic-gate len += strlen(mxhosts[i]) + 1; 5812*7c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 5813*7c478bd9Sstevel@tonic-gate len += strlen(s->s_hostsig.hs_sig) + 1; 5814*7c478bd9Sstevel@tonic-gate if (len < 0 || len >= MAXHOSTSIGNATURE) 5815*7c478bd9Sstevel@tonic-gate { 5816*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_WARNING, NOQID, "hostsignature for host '%s' exceeds maxlen (%d): %d", 5817*7c478bd9Sstevel@tonic-gate host, MAXHOSTSIGNATURE, len); 5818*7c478bd9Sstevel@tonic-gate len = MAXHOSTSIGNATURE; 5819*7c478bd9Sstevel@tonic-gate } 5820*7c478bd9Sstevel@tonic-gate p = sm_pmalloc_x(len); 5821*7c478bd9Sstevel@tonic-gate if (s->s_hostsig.hs_sig != NULL) 5822*7c478bd9Sstevel@tonic-gate { 5823*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, s->s_hostsig.hs_sig, len); 5824*7c478bd9Sstevel@tonic-gate sm_free(s->s_hostsig.hs_sig); /* XXX */ 5825*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = p; 5826*7c478bd9Sstevel@tonic-gate hl = strlen(p); 5827*7c478bd9Sstevel@tonic-gate p += hl; 5828*7c478bd9Sstevel@tonic-gate *p++ = prevsep; 5829*7c478bd9Sstevel@tonic-gate len -= hl + 1; 5830*7c478bd9Sstevel@tonic-gate } 5831*7c478bd9Sstevel@tonic-gate else 5832*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = p; 5833*7c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 5834*7c478bd9Sstevel@tonic-gate { 5835*7c478bd9Sstevel@tonic-gate hl = strlen(mxhosts[i]); 5836*7c478bd9Sstevel@tonic-gate if (len - 1 < hl || len <= 1) 5837*7c478bd9Sstevel@tonic-gate { 5838*7c478bd9Sstevel@tonic-gate /* force to drop out of outer loop */ 5839*7c478bd9Sstevel@tonic-gate len = -1; 5840*7c478bd9Sstevel@tonic-gate break; 5841*7c478bd9Sstevel@tonic-gate } 5842*7c478bd9Sstevel@tonic-gate if (i != 0) 5843*7c478bd9Sstevel@tonic-gate { 5844*7c478bd9Sstevel@tonic-gate if (mxprefs[i] == mxprefs[i - 1]) 5845*7c478bd9Sstevel@tonic-gate *p++ = ','; 5846*7c478bd9Sstevel@tonic-gate else 5847*7c478bd9Sstevel@tonic-gate *p++ = ':'; 5848*7c478bd9Sstevel@tonic-gate len--; 5849*7c478bd9Sstevel@tonic-gate } 5850*7c478bd9Sstevel@tonic-gate (void) sm_strlcpy(p, mxhosts[i], len); 5851*7c478bd9Sstevel@tonic-gate p += hl; 5852*7c478bd9Sstevel@tonic-gate len -= hl; 5853*7c478bd9Sstevel@tonic-gate } 5854*7c478bd9Sstevel@tonic-gate 5855*7c478bd9Sstevel@tonic-gate /* 5856*7c478bd9Sstevel@tonic-gate ** break out of loop if len exceeded MAXHOSTSIGNATURE 5857*7c478bd9Sstevel@tonic-gate ** because we won't have more space for further hosts 5858*7c478bd9Sstevel@tonic-gate ** anyway (separated by : in the .cf file). 5859*7c478bd9Sstevel@tonic-gate */ 5860*7c478bd9Sstevel@tonic-gate 5861*7c478bd9Sstevel@tonic-gate if (len < 0) 5862*7c478bd9Sstevel@tonic-gate break; 5863*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5864*7c478bd9Sstevel@tonic-gate *endp++ = sep; 5865*7c478bd9Sstevel@tonic-gate prevsep = sep; 5866*7c478bd9Sstevel@tonic-gate } 5867*7c478bd9Sstevel@tonic-gate makelower(s->s_hostsig.hs_sig); 5868*7c478bd9Sstevel@tonic-gate if (ConfigLevel < 2) 5869*7c478bd9Sstevel@tonic-gate _res.options = oldoptions; 5870*7c478bd9Sstevel@tonic-gate #else /* NAMED_BIND */ 5871*7c478bd9Sstevel@tonic-gate /* not using BIND -- the signature is just the host name */ 5872*7c478bd9Sstevel@tonic-gate /* 5873*7c478bd9Sstevel@tonic-gate ** 'host' points to storage that will be freed after we are 5874*7c478bd9Sstevel@tonic-gate ** done processing the current envelope, so we copy it. 5875*7c478bd9Sstevel@tonic-gate */ 5876*7c478bd9Sstevel@tonic-gate s->s_hostsig.hs_sig = sm_pstrdup_x(host); 5877*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */ 5878*7c478bd9Sstevel@tonic-gate if (tTd(17, 1)) 5879*7c478bd9Sstevel@tonic-gate sm_dprintf("hostsignature(%s) = %s\n", host, s->s_hostsig.hs_sig); 5880*7c478bd9Sstevel@tonic-gate return s->s_hostsig.hs_sig; 5881*7c478bd9Sstevel@tonic-gate } 5882*7c478bd9Sstevel@tonic-gate /* 5883*7c478bd9Sstevel@tonic-gate ** PARSE_HOSTSIGNATURE -- parse the "signature" and return MX host array. 5884*7c478bd9Sstevel@tonic-gate ** 5885*7c478bd9Sstevel@tonic-gate ** The signature describes how we are going to send this -- it 5886*7c478bd9Sstevel@tonic-gate ** can be just the hostname (for non-Internet hosts) or can be 5887*7c478bd9Sstevel@tonic-gate ** an ordered list of MX hosts which must be randomized for equal 5888*7c478bd9Sstevel@tonic-gate ** MX preference values. 5889*7c478bd9Sstevel@tonic-gate ** 5890*7c478bd9Sstevel@tonic-gate ** Parameters: 5891*7c478bd9Sstevel@tonic-gate ** sig -- the host signature. 5892*7c478bd9Sstevel@tonic-gate ** mxhosts -- array to populate. 5893*7c478bd9Sstevel@tonic-gate ** mailer -- mailer. 5894*7c478bd9Sstevel@tonic-gate ** 5895*7c478bd9Sstevel@tonic-gate ** Returns: 5896*7c478bd9Sstevel@tonic-gate ** The number of hosts inserted into mxhosts array. 5897*7c478bd9Sstevel@tonic-gate ** 5898*7c478bd9Sstevel@tonic-gate ** Side Effects: 5899*7c478bd9Sstevel@tonic-gate ** Randomizes equal MX preference hosts in mxhosts. 5900*7c478bd9Sstevel@tonic-gate */ 5901*7c478bd9Sstevel@tonic-gate 5902*7c478bd9Sstevel@tonic-gate static int 5903*7c478bd9Sstevel@tonic-gate parse_hostsignature(sig, mxhosts, mailer) 5904*7c478bd9Sstevel@tonic-gate char *sig; 5905*7c478bd9Sstevel@tonic-gate char **mxhosts; 5906*7c478bd9Sstevel@tonic-gate MAILER *mailer; 5907*7c478bd9Sstevel@tonic-gate { 5908*7c478bd9Sstevel@tonic-gate unsigned short curpref = 0; 5909*7c478bd9Sstevel@tonic-gate int nmx = 0, i, j; /* NOTE: i, j, and nmx must have same type */ 5910*7c478bd9Sstevel@tonic-gate char *hp, *endp; 5911*7c478bd9Sstevel@tonic-gate unsigned short prefer[MAXMXHOSTS]; 5912*7c478bd9Sstevel@tonic-gate long rndm[MAXMXHOSTS]; 5913*7c478bd9Sstevel@tonic-gate 5914*7c478bd9Sstevel@tonic-gate for (hp = sig; hp != NULL; hp = endp) 5915*7c478bd9Sstevel@tonic-gate { 5916*7c478bd9Sstevel@tonic-gate char sep = ':'; 5917*7c478bd9Sstevel@tonic-gate 5918*7c478bd9Sstevel@tonic-gate #if NETINET6 5919*7c478bd9Sstevel@tonic-gate if (*hp == '[') 5920*7c478bd9Sstevel@tonic-gate { 5921*7c478bd9Sstevel@tonic-gate endp = strchr(hp + 1, ']'); 5922*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5923*7c478bd9Sstevel@tonic-gate endp = strpbrk(endp + 1, ":,"); 5924*7c478bd9Sstevel@tonic-gate } 5925*7c478bd9Sstevel@tonic-gate else 5926*7c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 5927*7c478bd9Sstevel@tonic-gate #else /* NETINET6 */ 5928*7c478bd9Sstevel@tonic-gate endp = strpbrk(hp, ":,"); 5929*7c478bd9Sstevel@tonic-gate #endif /* NETINET6 */ 5930*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5931*7c478bd9Sstevel@tonic-gate { 5932*7c478bd9Sstevel@tonic-gate sep = *endp; 5933*7c478bd9Sstevel@tonic-gate *endp = '\0'; 5934*7c478bd9Sstevel@tonic-gate } 5935*7c478bd9Sstevel@tonic-gate 5936*7c478bd9Sstevel@tonic-gate mxhosts[nmx] = hp; 5937*7c478bd9Sstevel@tonic-gate prefer[nmx] = curpref; 5938*7c478bd9Sstevel@tonic-gate if (mci_match(hp, mailer)) 5939*7c478bd9Sstevel@tonic-gate rndm[nmx] = 0; 5940*7c478bd9Sstevel@tonic-gate else 5941*7c478bd9Sstevel@tonic-gate rndm[nmx] = get_random(); 5942*7c478bd9Sstevel@tonic-gate 5943*7c478bd9Sstevel@tonic-gate if (endp != NULL) 5944*7c478bd9Sstevel@tonic-gate { 5945*7c478bd9Sstevel@tonic-gate /* 5946*7c478bd9Sstevel@tonic-gate ** Since we don't have the original MX prefs, 5947*7c478bd9Sstevel@tonic-gate ** make our own. If the separator is a ':', that 5948*7c478bd9Sstevel@tonic-gate ** means the preference for the next host will be 5949*7c478bd9Sstevel@tonic-gate ** higher than this one, so simply increment curpref. 5950*7c478bd9Sstevel@tonic-gate */ 5951*7c478bd9Sstevel@tonic-gate 5952*7c478bd9Sstevel@tonic-gate if (sep == ':') 5953*7c478bd9Sstevel@tonic-gate curpref++; 5954*7c478bd9Sstevel@tonic-gate 5955*7c478bd9Sstevel@tonic-gate *endp++ = sep; 5956*7c478bd9Sstevel@tonic-gate } 5957*7c478bd9Sstevel@tonic-gate if (++nmx >= MAXMXHOSTS) 5958*7c478bd9Sstevel@tonic-gate break; 5959*7c478bd9Sstevel@tonic-gate } 5960*7c478bd9Sstevel@tonic-gate 5961*7c478bd9Sstevel@tonic-gate /* sort the records using the random factor for equal preferences */ 5962*7c478bd9Sstevel@tonic-gate for (i = 0; i < nmx; i++) 5963*7c478bd9Sstevel@tonic-gate { 5964*7c478bd9Sstevel@tonic-gate for (j = i + 1; j < nmx; j++) 5965*7c478bd9Sstevel@tonic-gate { 5966*7c478bd9Sstevel@tonic-gate /* 5967*7c478bd9Sstevel@tonic-gate ** List is already sorted by MX preference, only 5968*7c478bd9Sstevel@tonic-gate ** need to look for equal preference MX records 5969*7c478bd9Sstevel@tonic-gate */ 5970*7c478bd9Sstevel@tonic-gate 5971*7c478bd9Sstevel@tonic-gate if (prefer[i] < prefer[j]) 5972*7c478bd9Sstevel@tonic-gate break; 5973*7c478bd9Sstevel@tonic-gate 5974*7c478bd9Sstevel@tonic-gate if (prefer[i] > prefer[j] || 5975*7c478bd9Sstevel@tonic-gate (prefer[i] == prefer[j] && rndm[i] > rndm[j])) 5976*7c478bd9Sstevel@tonic-gate { 5977*7c478bd9Sstevel@tonic-gate register unsigned short tempp; 5978*7c478bd9Sstevel@tonic-gate register long tempr; 5979*7c478bd9Sstevel@tonic-gate register char *temp1; 5980*7c478bd9Sstevel@tonic-gate 5981*7c478bd9Sstevel@tonic-gate tempp = prefer[i]; 5982*7c478bd9Sstevel@tonic-gate prefer[i] = prefer[j]; 5983*7c478bd9Sstevel@tonic-gate prefer[j] = tempp; 5984*7c478bd9Sstevel@tonic-gate temp1 = mxhosts[i]; 5985*7c478bd9Sstevel@tonic-gate mxhosts[i] = mxhosts[j]; 5986*7c478bd9Sstevel@tonic-gate mxhosts[j] = temp1; 5987*7c478bd9Sstevel@tonic-gate tempr = rndm[i]; 5988*7c478bd9Sstevel@tonic-gate rndm[i] = rndm[j]; 5989*7c478bd9Sstevel@tonic-gate rndm[j] = tempr; 5990*7c478bd9Sstevel@tonic-gate } 5991*7c478bd9Sstevel@tonic-gate } 5992*7c478bd9Sstevel@tonic-gate } 5993*7c478bd9Sstevel@tonic-gate return nmx; 5994*7c478bd9Sstevel@tonic-gate } 5995*7c478bd9Sstevel@tonic-gate 5996*7c478bd9Sstevel@tonic-gate # if STARTTLS 5997*7c478bd9Sstevel@tonic-gate static SSL_CTX *clt_ctx = NULL; 5998*7c478bd9Sstevel@tonic-gate static bool tls_ok_clt = true; 5999*7c478bd9Sstevel@tonic-gate 6000*7c478bd9Sstevel@tonic-gate /* 6001*7c478bd9Sstevel@tonic-gate ** SETCLTTLS -- client side TLS: allow/disallow. 6002*7c478bd9Sstevel@tonic-gate ** 6003*7c478bd9Sstevel@tonic-gate ** Parameters: 6004*7c478bd9Sstevel@tonic-gate ** tls_ok -- should tls be done? 6005*7c478bd9Sstevel@tonic-gate ** 6006*7c478bd9Sstevel@tonic-gate ** Returns: 6007*7c478bd9Sstevel@tonic-gate ** none. 6008*7c478bd9Sstevel@tonic-gate ** 6009*7c478bd9Sstevel@tonic-gate ** Side Effects: 6010*7c478bd9Sstevel@tonic-gate ** sets tls_ok_clt (static variable in this module) 6011*7c478bd9Sstevel@tonic-gate */ 6012*7c478bd9Sstevel@tonic-gate 6013*7c478bd9Sstevel@tonic-gate void 6014*7c478bd9Sstevel@tonic-gate setclttls(tls_ok) 6015*7c478bd9Sstevel@tonic-gate bool tls_ok; 6016*7c478bd9Sstevel@tonic-gate { 6017*7c478bd9Sstevel@tonic-gate tls_ok_clt = tls_ok; 6018*7c478bd9Sstevel@tonic-gate return; 6019*7c478bd9Sstevel@tonic-gate } 6020*7c478bd9Sstevel@tonic-gate /* 6021*7c478bd9Sstevel@tonic-gate ** INITCLTTLS -- initialize client side TLS 6022*7c478bd9Sstevel@tonic-gate ** 6023*7c478bd9Sstevel@tonic-gate ** Parameters: 6024*7c478bd9Sstevel@tonic-gate ** tls_ok -- should tls initialization be done? 6025*7c478bd9Sstevel@tonic-gate ** 6026*7c478bd9Sstevel@tonic-gate ** Returns: 6027*7c478bd9Sstevel@tonic-gate ** succeeded? 6028*7c478bd9Sstevel@tonic-gate ** 6029*7c478bd9Sstevel@tonic-gate ** Side Effects: 6030*7c478bd9Sstevel@tonic-gate ** sets tls_ok_clt (static variable in this module) 6031*7c478bd9Sstevel@tonic-gate */ 6032*7c478bd9Sstevel@tonic-gate 6033*7c478bd9Sstevel@tonic-gate bool 6034*7c478bd9Sstevel@tonic-gate initclttls(tls_ok) 6035*7c478bd9Sstevel@tonic-gate bool tls_ok; 6036*7c478bd9Sstevel@tonic-gate { 6037*7c478bd9Sstevel@tonic-gate if (!tls_ok_clt) 6038*7c478bd9Sstevel@tonic-gate return false; 6039*7c478bd9Sstevel@tonic-gate tls_ok_clt = tls_ok; 6040*7c478bd9Sstevel@tonic-gate if (!tls_ok_clt) 6041*7c478bd9Sstevel@tonic-gate return false; 6042*7c478bd9Sstevel@tonic-gate if (clt_ctx != NULL) 6043*7c478bd9Sstevel@tonic-gate return true; /* already done */ 6044*7c478bd9Sstevel@tonic-gate tls_ok_clt = inittls(&clt_ctx, TLS_I_CLT, false, CltCertFile, 6045*7c478bd9Sstevel@tonic-gate CltKeyFile, CACertPath, CACertFile, DHParams); 6046*7c478bd9Sstevel@tonic-gate return tls_ok_clt; 6047*7c478bd9Sstevel@tonic-gate } 6048*7c478bd9Sstevel@tonic-gate 6049*7c478bd9Sstevel@tonic-gate /* 6050*7c478bd9Sstevel@tonic-gate ** STARTTLS -- try to start secure connection (client side) 6051*7c478bd9Sstevel@tonic-gate ** 6052*7c478bd9Sstevel@tonic-gate ** Parameters: 6053*7c478bd9Sstevel@tonic-gate ** m -- the mailer. 6054*7c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 6055*7c478bd9Sstevel@tonic-gate ** e -- the envelope. 6056*7c478bd9Sstevel@tonic-gate ** 6057*7c478bd9Sstevel@tonic-gate ** Returns: 6058*7c478bd9Sstevel@tonic-gate ** success? 6059*7c478bd9Sstevel@tonic-gate ** (maybe this should be some other code than EX_ 6060*7c478bd9Sstevel@tonic-gate ** that denotes which stage failed.) 6061*7c478bd9Sstevel@tonic-gate */ 6062*7c478bd9Sstevel@tonic-gate 6063*7c478bd9Sstevel@tonic-gate static int 6064*7c478bd9Sstevel@tonic-gate starttls(m, mci, e) 6065*7c478bd9Sstevel@tonic-gate MAILER *m; 6066*7c478bd9Sstevel@tonic-gate MCI *mci; 6067*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 6068*7c478bd9Sstevel@tonic-gate { 6069*7c478bd9Sstevel@tonic-gate int smtpresult; 6070*7c478bd9Sstevel@tonic-gate int result = 0; 6071*7c478bd9Sstevel@tonic-gate int rfd, wfd; 6072*7c478bd9Sstevel@tonic-gate SSL *clt_ssl = NULL; 6073*7c478bd9Sstevel@tonic-gate time_t tlsstart; 6074*7c478bd9Sstevel@tonic-gate 6075*7c478bd9Sstevel@tonic-gate if (clt_ctx == NULL && !initclttls(true)) 6076*7c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 6077*7c478bd9Sstevel@tonic-gate smtpmessage("STARTTLS", m, mci); 6078*7c478bd9Sstevel@tonic-gate 6079*7c478bd9Sstevel@tonic-gate /* get the reply */ 6080*7c478bd9Sstevel@tonic-gate smtpresult = reply(m, mci, e, TimeOuts.to_starttls, NULL, NULL, 6081*7c478bd9Sstevel@tonic-gate XS_STARTTLS); 6082*7c478bd9Sstevel@tonic-gate 6083*7c478bd9Sstevel@tonic-gate /* check return code from server */ 6084*7c478bd9Sstevel@tonic-gate if (smtpresult == 454) 6085*7c478bd9Sstevel@tonic-gate return EX_TEMPFAIL; 6086*7c478bd9Sstevel@tonic-gate if (smtpresult == 501) 6087*7c478bd9Sstevel@tonic-gate return EX_USAGE; 6088*7c478bd9Sstevel@tonic-gate if (smtpresult == -1) 6089*7c478bd9Sstevel@tonic-gate return smtpresult; 6090*7c478bd9Sstevel@tonic-gate if (smtpresult != 220) 6091*7c478bd9Sstevel@tonic-gate return EX_PROTOCOL; 6092*7c478bd9Sstevel@tonic-gate 6093*7c478bd9Sstevel@tonic-gate if (LogLevel > 13) 6094*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_INFO, NOQID, "STARTTLS=client, start=ok"); 6095*7c478bd9Sstevel@tonic-gate 6096*7c478bd9Sstevel@tonic-gate /* start connection */ 6097*7c478bd9Sstevel@tonic-gate if ((clt_ssl = SSL_new(clt_ctx)) == NULL) 6098*7c478bd9Sstevel@tonic-gate { 6099*7c478bd9Sstevel@tonic-gate if (LogLevel > 5) 6100*7c478bd9Sstevel@tonic-gate { 6101*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 6102*7c478bd9Sstevel@tonic-gate "STARTTLS=client, error: SSL_new failed"); 6103*7c478bd9Sstevel@tonic-gate if (LogLevel > 9) 6104*7c478bd9Sstevel@tonic-gate tlslogerr("client"); 6105*7c478bd9Sstevel@tonic-gate } 6106*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 6107*7c478bd9Sstevel@tonic-gate } 6108*7c478bd9Sstevel@tonic-gate 6109*7c478bd9Sstevel@tonic-gate rfd = sm_io_getinfo(mci->mci_in, SM_IO_WHAT_FD, NULL); 6110*7c478bd9Sstevel@tonic-gate wfd = sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD, NULL); 6111*7c478bd9Sstevel@tonic-gate 6112*7c478bd9Sstevel@tonic-gate /* SSL_clear(clt_ssl); ? */ 6113*7c478bd9Sstevel@tonic-gate if (rfd < 0 || wfd < 0 || 6114*7c478bd9Sstevel@tonic-gate (result = SSL_set_rfd(clt_ssl, rfd)) != 1 || 6115*7c478bd9Sstevel@tonic-gate (result = SSL_set_wfd(clt_ssl, wfd)) != 1) 6116*7c478bd9Sstevel@tonic-gate { 6117*7c478bd9Sstevel@tonic-gate if (LogLevel > 5) 6118*7c478bd9Sstevel@tonic-gate { 6119*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, NOQID, 6120*7c478bd9Sstevel@tonic-gate "STARTTLS=client, error: SSL_set_xfd failed=%d", 6121*7c478bd9Sstevel@tonic-gate result); 6122*7c478bd9Sstevel@tonic-gate if (LogLevel > 9) 6123*7c478bd9Sstevel@tonic-gate tlslogerr("client"); 6124*7c478bd9Sstevel@tonic-gate } 6125*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 6126*7c478bd9Sstevel@tonic-gate } 6127*7c478bd9Sstevel@tonic-gate SSL_set_connect_state(clt_ssl); 6128*7c478bd9Sstevel@tonic-gate tlsstart = curtime(); 6129*7c478bd9Sstevel@tonic-gate 6130*7c478bd9Sstevel@tonic-gate ssl_retry: 6131*7c478bd9Sstevel@tonic-gate if ((result = SSL_connect(clt_ssl)) <= 0) 6132*7c478bd9Sstevel@tonic-gate { 6133*7c478bd9Sstevel@tonic-gate int i; 6134*7c478bd9Sstevel@tonic-gate bool timedout; 6135*7c478bd9Sstevel@tonic-gate time_t left; 6136*7c478bd9Sstevel@tonic-gate time_t now = curtime(); 6137*7c478bd9Sstevel@tonic-gate struct timeval tv; 6138*7c478bd9Sstevel@tonic-gate 6139*7c478bd9Sstevel@tonic-gate /* what to do in this case? */ 6140*7c478bd9Sstevel@tonic-gate i = SSL_get_error(clt_ssl, result); 6141*7c478bd9Sstevel@tonic-gate 6142*7c478bd9Sstevel@tonic-gate /* 6143*7c478bd9Sstevel@tonic-gate ** For SSL_ERROR_WANT_{READ,WRITE}: 6144*7c478bd9Sstevel@tonic-gate ** There is not a complete SSL record available yet 6145*7c478bd9Sstevel@tonic-gate ** or there is only a partial SSL record removed from 6146*7c478bd9Sstevel@tonic-gate ** the network (socket) buffer into the SSL buffer. 6147*7c478bd9Sstevel@tonic-gate ** The SSL_connect will only succeed when a full 6148*7c478bd9Sstevel@tonic-gate ** SSL record is available (assuming a "real" error 6149*7c478bd9Sstevel@tonic-gate ** doesn't happen). To handle when a "real" error 6150*7c478bd9Sstevel@tonic-gate ** does happen the select is set for exceptions too. 6151*7c478bd9Sstevel@tonic-gate ** The connection may be re-negotiated during this time 6152*7c478bd9Sstevel@tonic-gate ** so both read and write "want errors" need to be handled. 6153*7c478bd9Sstevel@tonic-gate ** A select() exception loops back so that a proper SSL 6154*7c478bd9Sstevel@tonic-gate ** error message can be gotten. 6155*7c478bd9Sstevel@tonic-gate */ 6156*7c478bd9Sstevel@tonic-gate 6157*7c478bd9Sstevel@tonic-gate left = TimeOuts.to_starttls - (now - tlsstart); 6158*7c478bd9Sstevel@tonic-gate timedout = left <= 0; 6159*7c478bd9Sstevel@tonic-gate if (!timedout) 6160*7c478bd9Sstevel@tonic-gate { 6161*7c478bd9Sstevel@tonic-gate tv.tv_sec = left; 6162*7c478bd9Sstevel@tonic-gate tv.tv_usec = 0; 6163*7c478bd9Sstevel@tonic-gate } 6164*7c478bd9Sstevel@tonic-gate 6165*7c478bd9Sstevel@tonic-gate if (!timedout && FD_SETSIZE > 0 && 6166*7c478bd9Sstevel@tonic-gate (rfd >= FD_SETSIZE || 6167*7c478bd9Sstevel@tonic-gate (i == SSL_ERROR_WANT_WRITE && wfd >= FD_SETSIZE))) 6168*7c478bd9Sstevel@tonic-gate { 6169*7c478bd9Sstevel@tonic-gate if (LogLevel > 5) 6170*7c478bd9Sstevel@tonic-gate { 6171*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 6172*7c478bd9Sstevel@tonic-gate "STARTTLS=client, error: fd %d/%d too large", 6173*7c478bd9Sstevel@tonic-gate rfd, wfd); 6174*7c478bd9Sstevel@tonic-gate if (LogLevel > 8) 6175*7c478bd9Sstevel@tonic-gate tlslogerr("client"); 6176*7c478bd9Sstevel@tonic-gate } 6177*7c478bd9Sstevel@tonic-gate errno = EINVAL; 6178*7c478bd9Sstevel@tonic-gate goto tlsfail; 6179*7c478bd9Sstevel@tonic-gate } 6180*7c478bd9Sstevel@tonic-gate if (!timedout && i == SSL_ERROR_WANT_READ) 6181*7c478bd9Sstevel@tonic-gate { 6182*7c478bd9Sstevel@tonic-gate fd_set ssl_maskr, ssl_maskx; 6183*7c478bd9Sstevel@tonic-gate 6184*7c478bd9Sstevel@tonic-gate FD_ZERO(&ssl_maskr); 6185*7c478bd9Sstevel@tonic-gate FD_SET(rfd, &ssl_maskr); 6186*7c478bd9Sstevel@tonic-gate FD_ZERO(&ssl_maskx); 6187*7c478bd9Sstevel@tonic-gate FD_SET(rfd, &ssl_maskx); 6188*7c478bd9Sstevel@tonic-gate if (select(rfd + 1, &ssl_maskr, NULL, &ssl_maskx, &tv) 6189*7c478bd9Sstevel@tonic-gate > 0) 6190*7c478bd9Sstevel@tonic-gate goto ssl_retry; 6191*7c478bd9Sstevel@tonic-gate } 6192*7c478bd9Sstevel@tonic-gate if (!timedout && i == SSL_ERROR_WANT_WRITE) 6193*7c478bd9Sstevel@tonic-gate { 6194*7c478bd9Sstevel@tonic-gate fd_set ssl_maskw, ssl_maskx; 6195*7c478bd9Sstevel@tonic-gate 6196*7c478bd9Sstevel@tonic-gate FD_ZERO(&ssl_maskw); 6197*7c478bd9Sstevel@tonic-gate FD_SET(wfd, &ssl_maskw); 6198*7c478bd9Sstevel@tonic-gate FD_ZERO(&ssl_maskx); 6199*7c478bd9Sstevel@tonic-gate FD_SET(rfd, &ssl_maskx); 6200*7c478bd9Sstevel@tonic-gate if (select(wfd + 1, NULL, &ssl_maskw, &ssl_maskx, &tv) 6201*7c478bd9Sstevel@tonic-gate > 0) 6202*7c478bd9Sstevel@tonic-gate goto ssl_retry; 6203*7c478bd9Sstevel@tonic-gate } 6204*7c478bd9Sstevel@tonic-gate if (LogLevel > 5) 6205*7c478bd9Sstevel@tonic-gate { 6206*7c478bd9Sstevel@tonic-gate sm_syslog(LOG_ERR, e->e_id, 6207*7c478bd9Sstevel@tonic-gate "STARTTLS=client, error: connect failed=%d, SSL_error=%d, timedout=%d, errno=%d", 6208*7c478bd9Sstevel@tonic-gate result, i, (int) timedout, errno); 6209*7c478bd9Sstevel@tonic-gate if (LogLevel > 8) 6210*7c478bd9Sstevel@tonic-gate tlslogerr("client"); 6211*7c478bd9Sstevel@tonic-gate } 6212*7c478bd9Sstevel@tonic-gate tlsfail: 6213*7c478bd9Sstevel@tonic-gate SSL_free(clt_ssl); 6214*7c478bd9Sstevel@tonic-gate clt_ssl = NULL; 6215*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 6216*7c478bd9Sstevel@tonic-gate } 6217*7c478bd9Sstevel@tonic-gate mci->mci_ssl = clt_ssl; 6218*7c478bd9Sstevel@tonic-gate result = tls_get_info(mci->mci_ssl, false, mci->mci_host, 6219*7c478bd9Sstevel@tonic-gate &mci->mci_macro, true); 6220*7c478bd9Sstevel@tonic-gate 6221*7c478bd9Sstevel@tonic-gate /* switch to use TLS... */ 6222*7c478bd9Sstevel@tonic-gate if (sfdctls(&mci->mci_in, &mci->mci_out, mci->mci_ssl) == 0) 6223*7c478bd9Sstevel@tonic-gate return EX_OK; 6224*7c478bd9Sstevel@tonic-gate 6225*7c478bd9Sstevel@tonic-gate /* failure */ 6226*7c478bd9Sstevel@tonic-gate SSL_free(clt_ssl); 6227*7c478bd9Sstevel@tonic-gate clt_ssl = NULL; 6228*7c478bd9Sstevel@tonic-gate return EX_SOFTWARE; 6229*7c478bd9Sstevel@tonic-gate } 6230*7c478bd9Sstevel@tonic-gate /* 6231*7c478bd9Sstevel@tonic-gate ** ENDTLSCLT -- shutdown secure connection (client side) 6232*7c478bd9Sstevel@tonic-gate ** 6233*7c478bd9Sstevel@tonic-gate ** Parameters: 6234*7c478bd9Sstevel@tonic-gate ** mci -- the mailer connection info. 6235*7c478bd9Sstevel@tonic-gate ** 6236*7c478bd9Sstevel@tonic-gate ** Returns: 6237*7c478bd9Sstevel@tonic-gate ** success? 6238*7c478bd9Sstevel@tonic-gate */ 6239*7c478bd9Sstevel@tonic-gate 6240*7c478bd9Sstevel@tonic-gate static int 6241*7c478bd9Sstevel@tonic-gate endtlsclt(mci) 6242*7c478bd9Sstevel@tonic-gate MCI *mci; 6243*7c478bd9Sstevel@tonic-gate { 6244*7c478bd9Sstevel@tonic-gate int r; 6245*7c478bd9Sstevel@tonic-gate 6246*7c478bd9Sstevel@tonic-gate if (!bitset(MCIF_TLSACT, mci->mci_flags)) 6247*7c478bd9Sstevel@tonic-gate return EX_OK; 6248*7c478bd9Sstevel@tonic-gate r = endtls(mci->mci_ssl, "client"); 6249*7c478bd9Sstevel@tonic-gate mci->mci_flags &= ~MCIF_TLSACT; 6250*7c478bd9Sstevel@tonic-gate return r; 6251*7c478bd9Sstevel@tonic-gate } 6252*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS */ 6253*7c478bd9Sstevel@tonic-gate # if STARTTLS || SASL 6254*7c478bd9Sstevel@tonic-gate /* 6255*7c478bd9Sstevel@tonic-gate ** ISCLTFLGSET -- check whether client flag is set. 6256*7c478bd9Sstevel@tonic-gate ** 6257*7c478bd9Sstevel@tonic-gate ** Parameters: 6258*7c478bd9Sstevel@tonic-gate ** e -- envelope. 6259*7c478bd9Sstevel@tonic-gate ** flag -- flag to check in {client_flags} 6260*7c478bd9Sstevel@tonic-gate ** 6261*7c478bd9Sstevel@tonic-gate ** Returns: 6262*7c478bd9Sstevel@tonic-gate ** true iff flag is set. 6263*7c478bd9Sstevel@tonic-gate */ 6264*7c478bd9Sstevel@tonic-gate 6265*7c478bd9Sstevel@tonic-gate static bool 6266*7c478bd9Sstevel@tonic-gate iscltflgset(e, flag) 6267*7c478bd9Sstevel@tonic-gate ENVELOPE *e; 6268*7c478bd9Sstevel@tonic-gate int flag; 6269*7c478bd9Sstevel@tonic-gate { 6270*7c478bd9Sstevel@tonic-gate char *p; 6271*7c478bd9Sstevel@tonic-gate 6272*7c478bd9Sstevel@tonic-gate p = macvalue(macid("{client_flags}"), e); 6273*7c478bd9Sstevel@tonic-gate if (p == NULL) 6274*7c478bd9Sstevel@tonic-gate return false; 6275*7c478bd9Sstevel@tonic-gate for (; *p != '\0'; p++) 6276*7c478bd9Sstevel@tonic-gate { 6277*7c478bd9Sstevel@tonic-gate /* look for just this one flag */ 6278*7c478bd9Sstevel@tonic-gate if (*p == (char) flag) 6279*7c478bd9Sstevel@tonic-gate return true; 6280*7c478bd9Sstevel@tonic-gate } 6281*7c478bd9Sstevel@tonic-gate return false; 6282*7c478bd9Sstevel@tonic-gate } 6283*7c478bd9Sstevel@tonic-gate # endif /* STARTTLS || SASL */ 6284