📄 deliver.c
字号:
/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */# include "sendmail.h"# include <signal.h># include <sys/stat.h># include <errno.h># include <vfork.h>SCCSID(@(#)deliver.c 1.1 92/07/30 SMI); /* from UCB 5.21 5/5/88 */static struct mxinfo *mx; /* "current" mx record information */# ifndef MXDOMAINstruct mxinfo dummy_mx; /* dummy mx info when not used */# endif MXDOMAIN/*** DELIVER -- Deliver a message to a list of addresses.**** This routine delivers to everyone on the same host as the** user on the head of the list. It is clever about mailers** that don't handle multiple users. It is NOT guaranteed** that it will deliver to all these addresses however -- so** deliver should be called once for each address on the** list.**** Parameters:** e -- the envelope to deliver.** firstto -- head of the address list to deliver to.**** Returns:** zero -- successfully delivered.** else -- some failure, see ExitStat for more info.**** Side Effects:** The standard input is passed off to someone.*/deliver(e, firstto) register ENVELOPE *e; ADDRESS *firstto;{ char *host; /* host being sent to */ char *user; /* user being sent to */ char **pvp; int sizeleft; /* Bytes of argv left to work with */ int buflen; /* Temp */ register char **mvp; register char *p; register MAILER *m; /* mailer for this recipient */ ADDRESS *ctladdr; register ADDRESS *to = firstto; bool clever = FALSE; /* running user smtp to this mailer */ ADDRESS *tochain = NULL; /* chain of users in this mailer call */ int rcode; /* response code */ char *pv[MAXPV+1]; char tobuf[MAXLINE-50]; /* text line of to people */ char buf[MAXNAME]; char tfrombuf[MAXNAME]; /* translated from person */ extern bool checkcompat(); extern ADDRESS *getctladdr(); extern char *remotename(); errno = 0; if (bitset(QDONTSEND, to->q_flags)) return (0); m = to->q_mailer; host = to->q_host;# ifdef DEBUG if (tTd(10, 1)) printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n", m->m_mno, host, to->q_user);# endif DEBUG /* ** If this mailer is expensive, and if we don't want to make ** connections now, just mark these addresses and return. ** This is useful if we want to batch connections to ** reduce load. This will cause the messages to be ** queued up, and a daemon will come along to send the ** messages later. ** This should be on a per-mailer basis. */ if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) && !Verbose) { for (; to != NULL; to = to->q_next) { if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m) continue; to->q_flags |= QQUEUEUP|QDONTSEND; e->e_to = to->q_paddr; message(Arpa_Info, "queued"); if (LogLevel > 4) logdelivery("queued"); } e->e_to = NULL; return (0); } /* ** Do initial argv setup. ** Insert the mailer name. Notice that $x expansion is ** NOT done on the mailer name. Then, if the mailer has ** a picky -f flag, we insert it as appropriate. This ** code does not check for 'pv' overflow; this places a ** manifest lower limit of 4 for MAXPV. ** The from address rewrite is expected to make ** the address relative to the other end. */ /* rewrite from address, using rewriting rules */ expand("\001f", buf, &buf[sizeof buf - 1], e); (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE)); define('g', tfrombuf, e); /* translated sender address */ define('h', host, e); /* to host */ Errors = 0; pvp = pv; sizeleft = m->m_argvsize - 10; /* Leave some slop */ *pvp++ = m->m_argv[0]; sizeleft -= strlen(m->m_argv[0]) + 1; /* insert -f or -r flag as appropriate */ if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags))) { if (bitnset(M_FOPT, m->m_flags)) *pvp++ = "-f"; else *pvp++ = "-r"; expand("\001g", buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); sizeleft -= strlen(buf) + 1 + 2 + 1; } /* ** Append the other fixed parts of the argv. These run ** up to the first entry containing "$u". There can only ** be one of these, and there are only a few more slots ** in the pv after it. */ for (mvp = m->m_argv; (p = *++mvp) != NULL; ) { while ((p = index(p, '\001')) != NULL) if (*++p == 'u') break; if (p != NULL) break; /* this entry is safe -- go ahead and process it */ expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); sizeleft -= strlen(buf) + 1; if (pvp >= &pv[MAXPV - 3]) { syserr("Too many parameters to %s before $u", pv[0]); return (-1); } } /* ** If we have no substitution for the user name in the argument ** list, we know that we must supply the names otherwise -- and ** SMTP is the answer!! */ if (*mvp == NULL) { /* running SMTP */# ifdef SMTP clever = TRUE; *pvp = NULL;# else SMTP /* oops! we don't implement SMTP */ syserr("SMTP style mailer"); return (EX_SOFTWARE);# endif SMTP } /* ** At this point *mvp points to the argument with $u. We ** run through our address list and append all the addresses ** we can. If we run out of space, do not fret! We can ** always send another copy later. */ tobuf[0] = '\0'; e->e_to = tobuf; ctladdr = NULL; for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) break; /* if already sent or not for this host, don't send */ if (bitset(QDONTSEND, to->q_flags) || strcmp(to->q_host, host) != 0 || to->q_mailer != firstto->q_mailer) continue; /* avoid overflowing tobuf */ if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0) break;# ifdef DEBUG if (tTd(10, 1)) { printf("\nsend to "); printaddr(to, FALSE); }# endif DEBUG /* compute effective uid/gid when sending */ if (to->q_mailer == ProgMailer) ctladdr = getctladdr(to); user = to->q_user; e->e_to = to->q_paddr; /* ** Check to see that these people are allowed to ** talk to each other. */ if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) { NoReturn = TRUE; usrerr("Message is too large; %ld bytes max", m->m_maxsize); giveresponse(EX_UNAVAILABLE, m, e); to->q_flags |= QDONTSEND; continue; } if (!checkcompat(to)) { giveresponse(EX_UNAVAILABLE, m, e); to->q_flags |= QDONTSEND; continue; } /* ** Strip quote bits from names if the mailer is dumb ** about them. */ if (bitnset(M_STRIPQ, m->m_flags)) { stripquotes(user, TRUE); stripquotes(host, TRUE); } else { stripquotes(user, FALSE); stripquotes(host, FALSE); } /* hack attack -- delivermail compatibility */ if (m == ProgMailer && *user == '|') user++; /* ** If an error message has already been given, don't ** bother to send to this address. ** ** >>>>>>>>>> This clause assumes that the local mailer ** >> NOTE >> cannot do any further aliasing; that ** >>>>>>>>>> function is subsumed by sendmail. */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) { to->q_flags |= QDONTSEND; continue; } /* ** Expand out this user and see if it fits in argv. ** By definition, the first user always fits, else ** we would loop here for rather a long time. */ define('u', user, e); /* to user */ define('z', to->q_home, e); /* user's home */ expand(*mvp, buf, &buf[sizeof buf - 1], e); buflen = strlen(buf); if (buflen > sizeleft && tobuf[0] != '\0') break; /* Stop argv here */ /* ** We have decided that we are sending the message to ** this user now. First mark it to avoid resending ** later. */ to->q_flags |= QDONTSEND; /* ** See if this user name is "special". ** If the user name has a slash in it, assume that this ** is a file -- send it off without further ado. Note ** that this type of addresses is not processed along ** with the others, so we fudge on the To person. */ if (m == LocalMailer) { if (user[0] == '/') { rcode = mailfile(user, getctladdr(to)); giveresponse(rcode, m, e); markstats(e, to); continue; } } /* ** Add this user to mailer argv, and add it ** to the print list of recipients. */ /* link together the chain of recipients */ to->q_tchain = tochain; tochain = to; /* create list of users for error messages */ (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); if (!clever) { *pvp++ = newstr(buf); sizeleft -= buflen + 1; if (pvp >= &pv[MAXPV - 2]) { /* allow some space for trailing parms */ break; } } } /* see if any addresses still exist */ if (tobuf[0] == '\0') { define('g', (char *) NULL, e); return (0); } /* print out messages as full list */ e->e_to = tobuf + 1; /* ** Fill out any parameters after the $u parameter. ** Note that these are not checked against "sizeleft", thus are ** not counted in the mailer L= option. The L= value just has ** to subtract off the max length of any trailing parameters, ** or figure out how to move them to the front instead. */ while (!clever && *++mvp != NULL) { expand(*mvp, buf, &buf[sizeof buf - 1], e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV]) syserr("deliver: pv overflow after $u for %s", pv[0]); } *pvp++ = NULL; /* ** Call the mailer. ** The argument vector gets built, pipes ** are created as necessary, and we fork & exec as ** appropriate. ** If we are running SMTP, we just need to clean up. */ message(Arpa_Info, "Connecting to %s via %s...", host, m->m_name); if (ctladdr == NULL) ctladdr = &e->e_from;# ifdef SMTP if (clever) {# ifdef MXDOMAIN extern struct mxinfo *getmxrr();/* * Should make this dependent on some flag, or at least * if strcmp(m->m_mailer,"[TCP]") == 0 * or certainly host[0] != '\0' */ mx = getmxrr(host, MyHostName, &rcode); if (mx->mx_number >= 0) rcode = EX_OK;#else MXDOMAIN mx = &dummy_mx; mx->mx_number = 1; mx->mx_hosts[0] = host; rcode = EX_OK;#endif /* send the initial SMTP protocol */ if (rcode == EX_OK) rcode = smtpinit(m, pv); if (rcode == EX_OK) { /* send the recipient list */ tobuf[0] = '\0'; for (to = tochain; to != NULL; to = to->q_tchain) { int i; e->e_to = to->q_paddr; i = smtprcpt(to, m); if (i != EX_OK) { markfailure(e, to, i); giveresponse(i, m, e); } else { (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); } } /* now send the data */ if (tobuf[0] == '\0') e->e_to = NULL; else { e->e_to = tobuf + 1; rcode = smtpdata(m, e); } /* now close the connection */ smtpquit(m); } } else# endif SMTP rcode = sendoff(e, m, pv, ctladdr); /* ** Do final status disposal. ** We check for something in tobuf for the SMTP case. ** If we got a temporary failure, arrange to queue the ** addressees. */ if (tobuf[0] != '\0') giveresponse(rcode, m, e); if (rcode != EX_OK) { for (to = tochain; to != NULL; to = to->q_tchain) markfailure(e, to, rcode); } else for (to = tochain; to != NULL; to = to->q_tchain) markstats(e, to); errno = 0; define('g', (char *) NULL, e); return (rcode);}/*** MARKFAILURE -- mark a failure on a specific address.**** Parameters:** e -- the envelope we are sending.** q -- the address to mark.** rcode -- the code signifying the particular failure.**** Returns:** none.**** Side Effects:** marks the address (and possibly the envelope) with the** failure so that an error will be returned or** the message will be queued, as appropriate.*/markfailure(e, q, rcode) register ENVELOPE *e; register ADDRESS *q; int rcode;{ if (rcode == EX_OK) return; else if (rcode != EX_TEMPFAIL && rcode != EX_OSERR && rcode !=EX_IOERR) q->q_flags |= QBADADDR; else if (curtime() > e->e_ctime + TimeOut) { extern char *pintvl(); char buf[MAXLINE]; if (!bitset(EF_TIMEOUT, e->e_flags)) { (void) sprintf(buf, "Cannot send message for %s", pintvl(TimeOut, FALSE)); if (e->e_message != NULL) free(e->e_message); e->e_message = newstr(buf); message(Arpa_Info, buf); } q->q_flags |= QBADADDR; e->e_flags |= EF_TIMEOUT; } else q->q_flags |= QQUEUEUP;}/*** DOFORK -- do a fork, retrying a couple of times on failure.**** This MUST be a macro, since after a vfork we are running** two processes on the same stack!!!**** Parameters:** none.**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -