1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 11 #ifndef lint 12 #ifdef SMTP 13 static char sccsid[] = "@(#)usersmtp.c 5.20.1.1 (Berkeley) 02/26/92 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)usersmtp.c 5.20.1.1 (Berkeley) 02/26/92 (without SMTP)"; 16 #endif 17 #endif /* not lint */ 18 19 # include <sysexits.h> 20 # include <errno.h> 21 22 # ifdef SMTP 23 24 /* 25 ** USERSMTP -- run SMTP protocol from the user end. 26 ** 27 ** This protocol is described in RFC821. 28 */ 29 30 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 31 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 32 #define SMTPCLOSING 421 /* "Service Shutting Down" */ 33 34 char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 35 char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 36 char SmtpError[MAXLINE] = ""; /* save failure error messages */ 37 int SmtpPid; /* pid of mailer */ 38 39 /* following represents the state of the SMTP connection */ 40 int SmtpState; /* connection state, see below */ 41 42 #define SMTP_CLOSED 0 /* connection is closed */ 43 #define SMTP_OPEN 1 /* connection is open for business */ 44 #define SMTP_SSD 2 /* service shutting down */ 45 /* 46 ** SMTPINIT -- initialize SMTP. 47 ** 48 ** Opens the connection and sends the initial protocol. 49 ** 50 ** Parameters: 51 ** m -- mailer to create connection to. 52 ** pvp -- pointer to parameter vector to pass to 53 ** the mailer. 54 ** 55 ** Returns: 56 ** appropriate exit status -- EX_OK on success. 57 ** If not EX_OK, it should close the connection. 58 ** 59 ** Side Effects: 60 ** creates connection and sends initial protocol. 61 */ 62 63 jmp_buf CtxGreeting; 64 65 smtpinit(m, pvp) 66 struct mailer *m; 67 char **pvp; 68 { 69 register int r; 70 EVENT *gte; 71 register STAB *st; 72 char buf[MAXNAME]; 73 static int greettimeout(); 74 extern STAB *stab(); 75 76 /* 77 ** Open the connection to the mailer. 78 */ 79 80 if (SmtpState == SMTP_OPEN) 81 syserr("smtpinit: already open"); 82 83 SmtpState = SMTP_CLOSED; 84 SmtpError[0] = '\0'; 85 SmtpPhase = "user open"; 86 setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase); 87 mci = openmailer(m, pvp, (ADDRESS *) NULL, TRUE); 88 if (SmtpPid < 0) 89 { 90 if (tTd(18, 1)) 91 printf("smtpinit: cannot open %s: stat %d errno %d\n", 92 pvp[0], ExitStat, errno); 93 if (CurEnv->e_xfp != NULL) 94 { 95 register char *p; 96 extern char *errstring(); 97 extern char *statstring(); 98 99 if (errno == 0) 100 { 101 p = statstring(ExitStat); 102 fprintf(CurEnv->e_xfp, 103 "%.3s %s.%s... %s\n", 104 p, pvp[1], m->m_name, p); 105 } 106 else 107 { 108 r = errno; 109 fprintf(CurEnv->e_xfp, 110 "421 %s.%s... Deferred: %s\n", 111 pvp[1], m->m_name, errstring(errno)); 112 errno = r; 113 } 114 } 115 return (ExitStat); 116 } 117 SmtpState = SMTP_OPEN; 118 119 /* 120 ** Get the greeting message. 121 ** This should appear spontaneously. Give it five minutes to 122 ** happen. 123 */ 124 125 if (setjmp(CtxGreeting) != 0) 126 goto tempfail1; 127 gte = setevent((time_t) 300, greettimeout, 0); 128 SmtpPhase = "greeting wait"; 129 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 130 r = reply(m); 131 clrevent(gte); 132 if (r < 0 || REPLYTYPE(r) != 2) 133 goto tempfail1; 134 135 /* 136 ** Send the HELO command. 137 ** My mother taught me to always introduce myself. 138 */ 139 140 smtpmessage("HELO %s", m, MyHostName); 141 SmtpPhase = "HELO wait"; 142 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 143 r = reply(m); 144 if (r < 0) 145 goto tempfail1; 146 else if (REPLYTYPE(r) == 5) 147 goto unavailable; 148 else if (REPLYTYPE(r) != 2) 149 goto tempfail1; 150 151 /* 152 ** If this is expected to be another sendmail, send some internal 153 ** commands. 154 */ 155 156 if (bitnset(M_INTERNAL, m->m_flags)) 157 { 158 /* tell it to be verbose */ 159 smtpmessage("VERB", m); 160 r = reply(m); 161 if (r < 0) 162 goto tempfail2; 163 164 /* tell it we will be sending one transaction only */ 165 smtpmessage("ONEX", m); 166 r = reply(m); 167 if (r < 0) 168 goto tempfail2; 169 } 170 171 /* 172 ** Send the MAIL command. 173 ** Designates the sender. 174 */ 175 176 expand("\001<", buf, &buf[sizeof buf - 1], CurEnv); 177 if (CurEnv->e_from.q_mailer == LocalMailer || 178 !bitnset(M_FROMPATH, m->m_flags)) 179 { 180 smtpmessage("MAIL From:<%s>", m, buf); 181 } 182 else 183 { 184 smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName, 185 buf[0] == '@' ? ',' : ':', buf); 186 } 187 SmtpPhase = "MAIL wait"; 188 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 189 r = reply(m); 190 if (r < 0 || REPLYTYPE(r) == 4) 191 goto tempfail2; 192 else if (r == 250) 193 return (EX_OK); 194 else if (r == 552) 195 goto unavailable; 196 197 /* protocol error -- close up */ 198 smtpquit(m); 199 return (EX_PROTOCOL); 200 201 tempfail1: 202 /* log this as an error to avoid sure-to-be-void connections */ 203 st = stab(CurHostName, ST_MCONINFO + m->m_mno, ST_ENTER); 204 st->s_host.ho_exitstat = EX_TEMPFAIL; 205 st->s_host.ho_errno = errno; 206 207 tempfail2: 208 /* signal a temporary failure */ 209 smtpquit(m); 210 return (EX_TEMPFAIL); 211 212 unavailable: 213 /* signal service unavailable */ 214 smtpquit(m); 215 return (EX_UNAVAILABLE); 216 } 217 218 219 static 220 greettimeout() 221 { 222 /* timeout reading the greeting message */ 223 longjmp(CtxGreeting, 1); 224 } 225 /* 226 ** SMTPRCPT -- designate recipient. 227 ** 228 ** Parameters: 229 ** to -- address of recipient. 230 ** m -- the mailer we are sending to. 231 ** 232 ** Returns: 233 ** exit status corresponding to recipient status. 234 ** 235 ** Side Effects: 236 ** Sends the mail via SMTP. 237 */ 238 239 smtprcpt(to, m) 240 ADDRESS *to; 241 register MAILER *m; 242 { 243 register int r; 244 extern char *remotename(); 245 246 smtpmessage("RCPT To:<%s>", m, to->q_user); 247 248 SmtpPhase = "RCPT wait"; 249 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 250 r = reply(m); 251 if (r < 0 || REPLYTYPE(r) == 4) 252 return (EX_TEMPFAIL); 253 else if (REPLYTYPE(r) == 2) 254 return (EX_OK); 255 else if (r == 550 || r == 551 || r == 553) 256 return (EX_NOUSER); 257 else if (r == 552 || r == 554) 258 return (EX_UNAVAILABLE); 259 return (EX_PROTOCOL); 260 } 261 /* 262 ** SMTPDATA -- send the data and clean up the transaction. 263 ** 264 ** Parameters: 265 ** m -- mailer being sent to. 266 ** e -- the envelope for this message. 267 ** 268 ** Returns: 269 ** exit status corresponding to DATA command. 270 ** 271 ** Side Effects: 272 ** none. 273 */ 274 275 smtpdata(m, e) 276 struct mailer *m; 277 register ENVELOPE *e; 278 { 279 register int r; 280 281 /* 282 ** Send the data. 283 ** First send the command and check that it is ok. 284 ** Then send the data. 285 ** Follow it up with a dot to terminate. 286 ** Finally get the results of the transaction. 287 */ 288 289 /* send the command and check ok to proceed */ 290 smtpmessage("DATA", m); 291 SmtpPhase = "DATA wait"; 292 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 293 r = reply(m); 294 if (r < 0 || REPLYTYPE(r) == 4) 295 return (EX_TEMPFAIL); 296 else if (r == 554) 297 return (EX_UNAVAILABLE); 298 else if (r != 354) 299 return (EX_PROTOCOL); 300 301 /* now output the actual message */ 302 (*e->e_puthdr)(SmtpOut, m, CurEnv); 303 putline("\n", SmtpOut, m); 304 (*e->e_putbody)(SmtpOut, m, CurEnv); 305 306 /* terminate the message */ 307 fprintf(SmtpOut, ".%s", m->m_eol); 308 if (Verbose && !HoldErrs) 309 nmessage(Arpa_Info, ">>> ."); 310 311 /* check for the results of the transaction */ 312 SmtpPhase = "result wait"; 313 setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase); 314 r = reply(m); 315 if (r < 0 || REPLYTYPE(r) == 4) 316 return (EX_TEMPFAIL); 317 else if (r == 250) 318 return (EX_OK); 319 else if (r == 552 || r == 554) 320 return (EX_UNAVAILABLE); 321 return (EX_PROTOCOL); 322 } 323 /* 324 ** SMTPQUIT -- close the SMTP connection. 325 ** 326 ** Parameters: 327 ** m -- a pointer to the mailer. 328 ** 329 ** Returns: 330 ** none. 331 ** 332 ** Side Effects: 333 ** sends the final protocol and closes the connection. 334 */ 335 336 smtpquit(mci) 337 register MCONINFO *mci; 338 { 339 int i; 340 341 /* if the connection is already closed, don't bother */ 342 if (mci->mci_state == MCI_CLOSED) 343 return; 344 345 /* send the quit message if not a forced quit */ 346 if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD) 347 { 348 smtpmessage("QUIT", m); 349 (void) reply(m); 350 if (SmtpState == SMTP_CLOSED) 351 return; 352 } 353 354 /* now actually close the connection and pick up the zombie */ 355 i = endmailer(mci, m->m_argv[0]); 356 if (i != EX_OK) 357 syserr("smtpquit %s: stat %d", m->m_argv[0], i); 358 } 359 /* 360 ** REPLY -- read arpanet reply 361 ** 362 ** Parameters: 363 ** m -- the mailer we are reading the reply from. 364 ** 365 ** Returns: 366 ** reply code it reads. 367 ** 368 ** Side Effects: 369 ** flushes the mail file. 370 */ 371 372 reply(m) 373 MAILER *m; 374 { 375 (void) fflush(SmtpOut); 376 377 if (tTd(18, 1)) 378 printf("reply\n"); 379 380 /* 381 ** Read the input line, being careful not to hang. 382 */ 383 384 for (;;) 385 { 386 register int r; 387 register char *p; 388 389 /* actually do the read */ 390 if (CurEnv->e_xfp != NULL) 391 (void) fflush(CurEnv->e_xfp); /* for debugging */ 392 393 /* if we are in the process of closing just give the code */ 394 if (SmtpState == SMTP_CLOSED) 395 return (SMTPCLOSING); 396 397 /* get the line from the other side */ 398 p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn); 399 if (p == NULL) 400 { 401 extern char MsgBuf[]; /* err.c */ 402 extern char Arpa_TSyserr[]; /* conf.c */ 403 404 /* if the remote end closed early, fake an error */ 405 if (errno == 0) 406 # ifdef ECONNRESET 407 errno = ECONNRESET; 408 # else ECONNRESET 409 errno = EPIPE; 410 # endif ECONNRESET 411 412 message(Arpa_TSyserr, "reply: read error"); 413 /* if debugging, pause so we can see state */ 414 if (tTd(18, 100)) 415 pause(); 416 # ifdef LOG 417 syslog(LOG_INFO, "%s", &MsgBuf[4]); 418 # endif LOG 419 SmtpState = SMTP_CLOSED; 420 smtpquit(m); 421 return (-1); 422 } 423 fixcrlf(SmtpReplyBuffer, TRUE); 424 425 if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL) 426 { 427 /* serious error -- log the previous command */ 428 if (SmtpMsgBuffer[0] != '\0') 429 fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer); 430 SmtpMsgBuffer[0] = '\0'; 431 432 /* now log the message as from the other side */ 433 fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer); 434 } 435 436 /* display the input for verbose mode */ 437 if (Verbose && !HoldErrs) 438 nmessage(Arpa_Info, "%s", SmtpReplyBuffer); 439 440 /* if continuation is required, we can go on */ 441 if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0])) 442 continue; 443 444 /* decode the reply code */ 445 r = atoi(SmtpReplyBuffer); 446 447 /* extra semantics: 0xx codes are "informational" */ 448 if (r < 100) 449 continue; 450 451 /* reply code 421 is "Service Shutting Down" */ 452 if (r == SMTPCLOSING && SmtpState != SMTP_SSD) 453 { 454 /* send the quit protocol */ 455 SmtpState = SMTP_SSD; 456 smtpquit(m); 457 } 458 459 /* save temporary failure messages for posterity */ 460 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 461 (void) strcpy(SmtpError, &SmtpReplyBuffer[4]); 462 463 return (r); 464 } 465 } 466 /* 467 ** SMTPMESSAGE -- send message to server 468 ** 469 ** Parameters: 470 ** f -- format 471 ** m -- the mailer to control formatting. 472 ** a, b, c -- parameters 473 ** 474 ** Returns: 475 ** none. 476 ** 477 ** Side Effects: 478 ** writes message to SmtpOut. 479 */ 480 481 /*VARARGS1*/ 482 smtpmessage(f, m, a, b, c) 483 char *f; 484 MAILER *m; 485 { 486 (void) sprintf(SmtpMsgBuffer, f, a, b, c); 487 if (tTd(18, 1) || (Verbose && !HoldErrs)) 488 nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer); 489 if (SmtpOut != NULL) 490 fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, 491 m == 0 ? "\r\n" : m->m_eol); 492 } 493 494 # endif SMTP 495