122716Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642831Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822716Sdist 933731Sbostic # include "sendmail.h" 1022716Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef SMTP 13*57990Seric static char sccsid[] = "@(#)usersmtp.c 6.6 (Berkeley) 02/14/93 (with SMTP)"; 1433731Sbostic #else 15*57990Seric static char sccsid[] = "@(#)usersmtp.c 6.6 (Berkeley) 02/14/93 (without SMTP)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194684Seric # include <sysexits.h> 2021065Seric # include <errno.h> 214684Seric 2233731Sbostic # ifdef SMTP 234684Seric 244684Seric /* 259391Seric ** USERSMTP -- run SMTP protocol from the user end. 269391Seric ** 279391Seric ** This protocol is described in RFC821. 289391Seric */ 299391Seric 309391Seric #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 319391Seric #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 329391Seric #define SMTPCLOSING 421 /* "Service Shutting Down" */ 339391Seric 3414900Seric char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 3510054Seric char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 3621065Seric char SmtpError[MAXLINE] = ""; /* save failure error messages */ 3710054Seric int SmtpPid; /* pid of mailer */ 389391Seric /* 394865Seric ** SMTPINIT -- initialize SMTP. 404684Seric ** 414865Seric ** Opens the connection and sends the initial protocol. 424684Seric ** 434684Seric ** Parameters: 444865Seric ** m -- mailer to create connection to. 454865Seric ** pvp -- pointer to parameter vector to pass to 464865Seric ** the mailer. 474684Seric ** 484684Seric ** Returns: 4954967Seric ** none. 504684Seric ** 514684Seric ** Side Effects: 524865Seric ** creates connection and sends initial protocol. 534684Seric */ 544684Seric 5554967Seric smtpinit(m, mci, e) 564865Seric struct mailer *m; 5754967Seric register MCI *mci; 5853751Seric ENVELOPE *e; 594684Seric { 604865Seric register int r; 6114886Seric EVENT *gte; 6252107Seric extern STAB *stab(); 634684Seric 6457379Seric if (tTd(17, 1)) 6557379Seric { 6657379Seric printf("smtpinit "); 6757379Seric mci_dump(mci); 6857379Seric } 6957379Seric 704865Seric /* 714865Seric ** Open the connection to the mailer. 724865Seric */ 734684Seric 7421065Seric SmtpError[0] = '\0'; 7557379Seric CurHostName = mci->mci_host; /* XXX UGLY XXX */ 7654967Seric switch (mci->mci_state) 776051Seric { 7854967Seric case MCIS_ACTIVE: 7954967Seric /* need to clear old information */ 8054967Seric smtprset(m, mci, e); 8157734Seric /* fall through */ 8215139Seric 8354967Seric case MCIS_OPEN: 8454967Seric return; 8554967Seric 8654967Seric case MCIS_ERROR: 8754967Seric case MCIS_SSD: 8854967Seric /* shouldn't happen */ 8954967Seric smtpquit(m, mci, e); 9057734Seric /* fall through */ 9154967Seric 9254967Seric case MCIS_CLOSED: 9354967Seric syserr("smtpinit: state CLOSED"); 9454967Seric return; 9554967Seric 9654967Seric case MCIS_OPENING: 9754967Seric break; 986051Seric } 994796Seric 10057379Seric SmtpPhase = mci->mci_phase = "user open"; 10154967Seric mci->mci_state = MCIS_OPENING; 10254967Seric 1034865Seric /* 1044865Seric ** Get the greeting message. 10514913Seric ** This should appear spontaneously. Give it five minutes to 10614886Seric ** happen. 1074865Seric */ 1084797Seric 10957379Seric SmtpPhase = mci->mci_phase = "greeting wait"; 11053751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 11157379Seric r = reply(m, mci, e, (time_t) 300); 1128005Seric if (r < 0 || REPLYTYPE(r) != 2) 11352104Seric goto tempfail1; 1144684Seric 1154865Seric /* 1164976Seric ** Send the HELO command. 1177963Seric ** My mother taught me to always introduce myself. 1184976Seric */ 1194976Seric 12053751Seric smtpmessage("HELO %s", m, mci, MyHostName); 12157379Seric SmtpPhase = mci->mci_phase = "HELO wait"; 12253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 12357379Seric r = reply(m, mci, e, ReadTimeout); 1248005Seric if (r < 0) 12552104Seric goto tempfail1; 1268005Seric else if (REPLYTYPE(r) == 5) 12714913Seric goto unavailable; 1287963Seric else if (REPLYTYPE(r) != 2) 12952104Seric goto tempfail1; 1304976Seric 1314976Seric /* 1329315Seric ** If this is expected to be another sendmail, send some internal 1339315Seric ** commands. 1349315Seric */ 1359315Seric 13610688Seric if (bitnset(M_INTERNAL, m->m_flags)) 1379315Seric { 1389315Seric /* tell it to be verbose */ 13953751Seric smtpmessage("VERB", m, mci); 14057379Seric r = reply(m, mci, e, ReadTimeout); 1419315Seric if (r < 0) 14252104Seric goto tempfail2; 1439315Seric } 1449315Seric 14553751Seric mci->mci_state = MCIS_OPEN; 14654967Seric return; 14753751Seric 14853751Seric tempfail1: 14953751Seric tempfail2: 15053751Seric mci->mci_exitstat = EX_TEMPFAIL; 15157379Seric if (mci->mci_errno == 0) 15257379Seric mci->mci_errno = errno; 15357379Seric if (mci->mci_state != MCIS_CLOSED) 15457379Seric smtpquit(m, mci, e); 15554967Seric return; 15653751Seric 15753751Seric unavailable: 15853751Seric mci->mci_exitstat = EX_UNAVAILABLE; 15953751Seric mci->mci_errno = errno; 16053751Seric smtpquit(m, mci, e); 16154967Seric return; 16253751Seric } 16353751Seric 16453751Seric smtpmailfrom(m, mci, e) 16553751Seric struct mailer *m; 16654967Seric MCI *mci; 16753751Seric ENVELOPE *e; 16853751Seric { 16953751Seric int r; 17053751Seric char buf[MAXNAME]; 17153751Seric 17257943Seric if (tTd(17, 2)) 17357943Seric printf("smtpmailfrom: CurHost=%s\n", CurHostName); 17457943Seric 1759315Seric /* 1764865Seric ** Send the MAIL command. 1774865Seric ** Designates the sender. 1784865Seric */ 1794796Seric 18053751Seric mci->mci_state = MCIS_ACTIVE; 18153751Seric 18253751Seric expand("\001<", buf, &buf[sizeof buf - 1], e); 18353751Seric if (e->e_from.q_mailer == LocalMailer || 18410688Seric !bitnset(M_FROMPATH, m->m_flags)) 1858436Seric { 18653751Seric smtpmessage("MAIL From:<%s>", m, mci, buf); 1878436Seric } 1888436Seric else 1898436Seric { 19053751Seric smtpmessage("MAIL From:<@%s%c%s>", m, mci, MyHostName, 19110308Seric buf[0] == '@' ? ',' : ':', buf); 1928436Seric } 19357379Seric SmtpPhase = mci->mci_phase = "MAIL wait"; 19453751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 19557379Seric r = reply(m, mci, e, ReadTimeout); 1968005Seric if (r < 0 || REPLYTYPE(r) == 4) 19753751Seric { 19853751Seric mci->mci_exitstat = EX_TEMPFAIL; 19953751Seric mci->mci_errno = errno; 20053751Seric smtpquit(m, mci, e); 20153751Seric return EX_TEMPFAIL; 20253751Seric } 2037963Seric else if (r == 250) 20453751Seric { 20553751Seric mci->mci_exitstat = EX_OK; 20653751Seric return EX_OK; 20753751Seric } 2087963Seric else if (r == 552) 20953751Seric { 21053751Seric /* signal service unavailable */ 21153751Seric mci->mci_exitstat = EX_UNAVAILABLE; 21253751Seric smtpquit(m, mci, e); 21353751Seric return EX_UNAVAILABLE; 21453751Seric } 21514913Seric 21614913Seric /* protocol error -- close up */ 21753751Seric smtpquit(m, mci, e); 21853751Seric mci->mci_exitstat = EX_PROTOCOL; 21953751Seric return EX_PROTOCOL; 2204684Seric } 2214684Seric /* 2224976Seric ** SMTPRCPT -- designate recipient. 2234797Seric ** 2244797Seric ** Parameters: 2254865Seric ** to -- address of recipient. 22610175Seric ** m -- the mailer we are sending to. 22757379Seric ** mci -- the connection info for this transaction. 22857379Seric ** e -- the envelope for this transaction. 2294797Seric ** 2304797Seric ** Returns: 2314865Seric ** exit status corresponding to recipient status. 2324797Seric ** 2334797Seric ** Side Effects: 2344865Seric ** Sends the mail via SMTP. 2354797Seric */ 2364797Seric 23753751Seric smtprcpt(to, m, mci, e) 2384865Seric ADDRESS *to; 23910175Seric register MAILER *m; 24054967Seric MCI *mci; 24153751Seric ENVELOPE *e; 2424797Seric { 2434797Seric register int r; 2444797Seric 24553751Seric smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 2464865Seric 24757379Seric SmtpPhase = mci->mci_phase = "RCPT wait"; 24853751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 24957379Seric r = reply(m, mci, e, ReadTimeout); 2508005Seric if (r < 0 || REPLYTYPE(r) == 4) 2514865Seric return (EX_TEMPFAIL); 2527963Seric else if (REPLYTYPE(r) == 2) 2537963Seric return (EX_OK); 2547964Seric else if (r == 550 || r == 551 || r == 553) 2557964Seric return (EX_NOUSER); 2567964Seric else if (r == 552 || r == 554) 2577964Seric return (EX_UNAVAILABLE); 2587964Seric return (EX_PROTOCOL); 2594797Seric } 2604797Seric /* 26110175Seric ** SMTPDATA -- send the data and clean up the transaction. 2624684Seric ** 2634684Seric ** Parameters: 2644865Seric ** m -- mailer being sent to. 2656980Seric ** e -- the envelope for this message. 2664684Seric ** 2674684Seric ** Returns: 2684976Seric ** exit status corresponding to DATA command. 2694684Seric ** 2704684Seric ** Side Effects: 2714865Seric ** none. 2724684Seric */ 2734684Seric 27453740Seric smtpdata(m, mci, e) 2754865Seric struct mailer *m; 27654967Seric register MCI *mci; 2776980Seric register ENVELOPE *e; 2784684Seric { 2794684Seric register int r; 2804684Seric 2814797Seric /* 2824797Seric ** Send the data. 28310175Seric ** First send the command and check that it is ok. 28410175Seric ** Then send the data. 28510175Seric ** Follow it up with a dot to terminate. 28610175Seric ** Finally get the results of the transaction. 2874797Seric */ 2884797Seric 28910175Seric /* send the command and check ok to proceed */ 29053751Seric smtpmessage("DATA", m, mci); 29157379Seric SmtpPhase = mci->mci_phase = "DATA wait"; 29253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 29357379Seric r = reply(m, mci, e, ReadTimeout); 2948005Seric if (r < 0 || REPLYTYPE(r) == 4) 295*57990Seric { 296*57990Seric smtpquit(m, mci, e); 2974797Seric return (EX_TEMPFAIL); 298*57990Seric } 2997963Seric else if (r == 554) 300*57990Seric { 301*57990Seric smtprset(m, mci, e); 3027963Seric return (EX_UNAVAILABLE); 303*57990Seric } 3047963Seric else if (r != 354) 305*57990Seric { 306*57990Seric smtprset(m, mci, e); 3077964Seric return (EX_PROTOCOL); 308*57990Seric } 30910175Seric 31010175Seric /* now output the actual message */ 31153751Seric (*e->e_puthdr)(mci->mci_out, m, e); 31253740Seric putline("\n", mci->mci_out, m); 31353751Seric (*e->e_putbody)(mci->mci_out, m, e); 31410175Seric 31510175Seric /* terminate the message */ 31653740Seric fprintf(mci->mci_out, ".%s", m->m_eol); 31710215Seric if (Verbose && !HoldErrs) 31810215Seric nmessage(Arpa_Info, ">>> ."); 31910175Seric 32010175Seric /* check for the results of the transaction */ 32157379Seric SmtpPhase = mci->mci_phase = "result wait"; 32253751Seric setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 32357379Seric r = reply(m, mci, e, ReadTimeout); 32453751Seric if (r < 0) 325*57990Seric { 326*57990Seric smtpquit(m, mci, e); 3274797Seric return (EX_TEMPFAIL); 328*57990Seric } 32953751Seric mci->mci_state = MCIS_OPEN; 33053751Seric if (REPLYTYPE(r) == 4) 33153751Seric return (EX_TEMPFAIL); 3327963Seric else if (r == 250) 3337963Seric return (EX_OK); 3347963Seric else if (r == 552 || r == 554) 3357963Seric return (EX_UNAVAILABLE); 3367964Seric return (EX_PROTOCOL); 3374684Seric } 3384684Seric /* 3394865Seric ** SMTPQUIT -- close the SMTP connection. 3404865Seric ** 3414865Seric ** Parameters: 34215535Seric ** m -- a pointer to the mailer. 3434865Seric ** 3444865Seric ** Returns: 3454865Seric ** none. 3464865Seric ** 3474865Seric ** Side Effects: 3484865Seric ** sends the final protocol and closes the connection. 3494865Seric */ 3504865Seric 35153751Seric smtpquit(m, mci, e) 35253751Seric register MAILER *m; 35354967Seric register MCI *mci; 35453751Seric ENVELOPE *e; 3554865Seric { 3569391Seric int i; 3574865Seric 35854967Seric /* send the quit message if we haven't gotten I/O error */ 35953751Seric if (mci->mci_state != MCIS_ERROR) 3609391Seric { 36153751Seric smtpmessage("QUIT", m, mci); 36257379Seric (void) reply(m, mci, e, ReadTimeout); 36353740Seric if (mci->mci_state == MCIS_CLOSED) 36410159Seric return; 3659391Seric } 3669391Seric 36752676Seric /* now actually close the connection and pick up the zombie */ 36852676Seric i = endmailer(mci, m->m_argv[0]); 3699391Seric if (i != EX_OK) 37015535Seric syserr("smtpquit %s: stat %d", m->m_argv[0], i); 3714865Seric } 3724865Seric /* 37354967Seric ** SMTPRSET -- send a RSET (reset) command 37454967Seric */ 37554967Seric 37654967Seric smtprset(m, mci, e) 37754967Seric register MAILER *m; 37854967Seric register MCI *mci; 37954967Seric ENVELOPE *e; 38054967Seric { 38154967Seric int r; 38254967Seric 38354967Seric smtpmessage("RSET", m, mci); 38457734Seric r = reply(m, mci, e, (time_t) 300); 38557734Seric if (r < 0) 38657734Seric mci->mci_state = MCIS_ERROR; 38754967Seric else if (REPLYTYPE(r) == 2) 38857734Seric { 38957734Seric mci->mci_state = MCIS_OPEN; 39057734Seric return; 39157734Seric } 39257734Seric smtpquit(m, mci, e); 39354967Seric } 39454967Seric /* 39554967Seric ** SMTPNOOP -- send a NOOP (no operation) command to check the connection state 39654967Seric */ 39754967Seric 39854967Seric smtpnoop(mci) 39954967Seric register MCI *mci; 40054967Seric { 40154967Seric int r; 40254967Seric MAILER *m = mci->mci_mailer; 40354967Seric extern ENVELOPE BlankEnvelope; 40454967Seric ENVELOPE *e = &BlankEnvelope; 40554967Seric 40654967Seric smtpmessage("NOOP", m, mci); 40757379Seric r = reply(m, mci, e, ReadTimeout); 40854967Seric if (REPLYTYPE(r) != 2) 40954967Seric smtpquit(m, mci, e); 41054967Seric return r; 41154967Seric } 41254967Seric /* 4134684Seric ** REPLY -- read arpanet reply 4144684Seric ** 4154684Seric ** Parameters: 41610175Seric ** m -- the mailer we are reading the reply from. 41757379Seric ** mci -- the mailer connection info structure. 41857379Seric ** e -- the current envelope. 41957379Seric ** timeout -- the timeout for reads. 4204684Seric ** 4214684Seric ** Returns: 4224684Seric ** reply code it reads. 4234684Seric ** 4244684Seric ** Side Effects: 4254684Seric ** flushes the mail file. 4264684Seric */ 4274684Seric 42857379Seric reply(m, mci, e, timeout) 42953751Seric MAILER *m; 43054967Seric MCI *mci; 43153751Seric ENVELOPE *e; 4324684Seric { 43357379Seric if (mci->mci_out != NULL) 43457379Seric (void) fflush(mci->mci_out); 4354684Seric 4367677Seric if (tTd(18, 1)) 4374796Seric printf("reply\n"); 4384796Seric 4397356Seric /* 4407356Seric ** Read the input line, being careful not to hang. 4417356Seric */ 4427356Seric 4434684Seric for (;;) 4444684Seric { 4454684Seric register int r; 4467356Seric register char *p; 44753751Seric extern time_t curtime(); 4484684Seric 4497685Seric /* actually do the read */ 45053751Seric if (e->e_xfp != NULL) 45153751Seric (void) fflush(e->e_xfp); /* for debugging */ 4527356Seric 45310054Seric /* if we are in the process of closing just give the code */ 45453740Seric if (mci->mci_state == MCIS_CLOSED) 45510054Seric return (SMTPCLOSING); 45610054Seric 45710054Seric /* get the line from the other side */ 45857379Seric p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, mci->mci_in, 45957379Seric timeout); 46053751Seric mci->mci_lastuse = curtime(); 46153751Seric 46210054Seric if (p == NULL) 46310131Seric { 46410148Seric extern char MsgBuf[]; /* err.c */ 46510148Seric extern char Arpa_TSyserr[]; /* conf.c */ 46610148Seric 46721065Seric /* if the remote end closed early, fake an error */ 46821065Seric if (errno == 0) 46921065Seric # ifdef ECONNRESET 47021065Seric errno = ECONNRESET; 47156795Seric # else /* ECONNRESET */ 47221065Seric errno = EPIPE; 47356795Seric # endif /* ECONNRESET */ 47421065Seric 47557379Seric mci->mci_errno = errno; 47657642Seric mci->mci_exitstat = EX_TEMPFAIL; 47757642Seric message(Arpa_TSyserr, "%s: reply: read error from %s", 47857642Seric e->e_id == NULL ? "NOQUEUE" : e->e_id, 47957203Seric mci->mci_host); 48010420Seric /* if debugging, pause so we can see state */ 48110420Seric if (tTd(18, 100)) 48210420Seric pause(); 48310148Seric # ifdef LOG 48457203Seric if (LogLevel > 0) 48557203Seric syslog(LOG_INFO, "%s", &MsgBuf[4]); 48656795Seric # endif /* LOG */ 48754967Seric mci->mci_state = MCIS_ERROR; 48853751Seric smtpquit(m, mci, e); 48910054Seric return (-1); 49010131Seric } 49110054Seric fixcrlf(SmtpReplyBuffer, TRUE); 49210054Seric 49356795Seric if (e->e_xfp != NULL && strchr("45", SmtpReplyBuffer[0]) != NULL) 49414900Seric { 49514900Seric /* serious error -- log the previous command */ 49614900Seric if (SmtpMsgBuffer[0] != '\0') 49753751Seric fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 49814900Seric SmtpMsgBuffer[0] = '\0'; 49914900Seric 50014900Seric /* now log the message as from the other side */ 50153751Seric fprintf(e->e_xfp, "<<< %s\n", SmtpReplyBuffer); 50214900Seric } 50314900Seric 50414900Seric /* display the input for verbose mode */ 5057229Seric if (Verbose && !HoldErrs) 5069391Seric nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 5077356Seric 5087356Seric /* if continuation is required, we can go on */ 5099391Seric if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 5104684Seric continue; 5117356Seric 5127356Seric /* decode the reply code */ 5139391Seric r = atoi(SmtpReplyBuffer); 5147356Seric 5157356Seric /* extra semantics: 0xx codes are "informational" */ 5164684Seric if (r < 100) 5174684Seric continue; 5187356Seric 5199391Seric /* reply code 421 is "Service Shutting Down" */ 52053740Seric if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 5219391Seric { 52210054Seric /* send the quit protocol */ 52353740Seric mci->mci_state = MCIS_SSD; 52453751Seric smtpquit(m, mci, e); 5259391Seric } 5269391Seric 52721065Seric /* save temporary failure messages for posterity */ 52821065Seric if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 52921065Seric (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 53021065Seric 5314684Seric return (r); 5324684Seric } 5334684Seric } 5344796Seric /* 5354865Seric ** SMTPMESSAGE -- send message to server 5364796Seric ** 5374796Seric ** Parameters: 5384796Seric ** f -- format 53910175Seric ** m -- the mailer to control formatting. 5404796Seric ** a, b, c -- parameters 5414796Seric ** 5424796Seric ** Returns: 5434796Seric ** none. 5444796Seric ** 5454796Seric ** Side Effects: 54653740Seric ** writes message to mci->mci_out. 5474796Seric */ 5484796Seric 5494865Seric /*VARARGS1*/ 55057642Seric #ifdef __STDC__ 55157642Seric smtpmessage(char *f, MAILER *m, MCI *mci, ...) 55257642Seric #else 55357642Seric smtpmessage(f, m, mci, va_alist) 5544796Seric char *f; 55510175Seric MAILER *m; 55654967Seric MCI *mci; 55757642Seric va_dcl 55857642Seric #endif 5594796Seric { 56056852Seric VA_LOCAL_DECL 56156852Seric 56257135Seric VA_START(mci); 56356852Seric (void) vsprintf(SmtpMsgBuffer, f, ap); 56456852Seric VA_END; 5657677Seric if (tTd(18, 1) || (Verbose && !HoldErrs)) 56614900Seric nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 56753740Seric if (mci->mci_out != NULL) 56853740Seric fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 56954967Seric m == NULL ? "\r\n" : m->m_eol); 5704796Seric } 5715182Seric 57256795Seric # endif /* SMTP */ 573